Repository: eegsynth/eegsynth (language: python)

Issue:
some modules bypass the patching in the ini file 
in `EEGsynth.py` the patch class has month others the following methods:

>     The following methods get either the values from the ini file or Redis
>       patch.getfloat(section, item, multiple=False, default=None)
>       patch.getint(section, item, multiple=False, default=None)
>       patch.getstring(section, item, multiple=False, default=None)
> 
>     The following methods get the values from Redis
>       patch.getfloat(channel, default=None)
>       patch.getint(channel, default=None)
>       patch.getstring(channel, default=None)


The ones that get the value directly from Redis bypass the ini file in a way, i.e., they construct their own channel name. I think that is suboptimal, better would be to do the patching in a consistent manner.

 
support pure-python Redis alternative in EEGsynth.patch
The most significant change is that code has moved from all modules to the general `EEGsynth.patch()` object. 

This PR includes a pure-python Redis server+client alternative based on [ZeroMQ](http://zeromq.org/). It only supports the Redis functionality that we actually use. I tested it with many patches and it all seems to work as expected. I did not notice any performance impact. The default for now remains to use Redis, this can be controlled with the section
 
```
[general]
broker=redis # or zeromq
```

This PR also includes improved parsing of command-line options, specifically for the `--general-broker` and `--general-debug` options, although others can in principle be added with a single line of code.

 


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 32279e04..697a0dce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,3 +66,4 @@ dist/*
 
 # Test patches
 patches/test_*
+eegsynth.spec
diff --git a/bin/eegsynth.py b/bin/eegsynth.py
index 7602f8a8..939a9c87 100755
--- a/bin/eegsynth.py
+++ b/bin/eegsynth.py
@@ -50,16 +50,21 @@
 # eegsynth/module contains the modules
 sys.path.insert(0, os.path.join(path, '..'))
 
+
 # the module starts as soon as it is instantiated
 # optional command-line arguments can be passed to specify the ini file
 def _start(module, args=None):
     module(args)
 
+
 def _main():
     """Parse command line options and start the EEGsynth modules for the specified patch.
     """
 
     parser = argparse.ArgumentParser()
+    parser.add_argument("--general-broker", default=None, help="general broker")
+    parser.add_argument("--general-debug", default=None, help="general debug")
+    parser.add_argument("--general-delay", default=None, help="general delay")
     parser.add_argument("inifile", nargs='+', help="configuration file for a patch")
     args = parser.parse_args()
 
@@ -70,6 +75,9 @@ def _main():
     args.inifile = [glob(x) for x in args.inifile]
     args.inifile = [item for sublist in args.inifile for item in sublist]
 
+    if len(args.inifile)==0:
+        raise RuntimeError('incorrect command line argument')
+        
     for file_or_dir in args.inifile:
         if os.path.isfile(file_or_dir):
             if not file_or_dir.endswith('.ini'):
@@ -78,26 +86,38 @@ def _main():
         else:
             raise RuntimeError('incorrect command line argument ' + file_or_dir)
 
-    # ignore the EEGsynth modules that are not implemented in Python but that do have an ini file
-    inifiles = [file for file in inifiles if not file.endswith('redis.ini')]
-    inifiles = [file for file in inifiles if not file.endswith('openbci2ft.ini')]
+    # convert the command-line arguments in a dict
+    args = vars(args)
+    # remove empty items
+    args = {k:v for k,v in args.items() if v}
 
     # this will contain a list of processes
     process = []
 
-    for file in inifiles:
-        module = os.path.split(file)[-1]        # keep only the filename
+    for inifile in inifiles:
+        module = os.path.split(inifile)[-1]     # keep only the filename
         module = os.path.splitext(module)[0]    # remove the ini extension
         module = module.split('-')[0]           # remove whatever comes after a "-" separator
         module = module.split('_')[0]           # remove whatever comes after a "_" separator
 
+        # convert the string in a reference to the corresponding class
+        # as soon as an object of the class is instantiated, the module will start
+
         # import the class that implements the specific module from eegsynth/module
         object = import_module('module.' + module)
 
-        # convert the string in a reference to the corresponding class
-        # as soon as an object of the class is instantiated, the module will start
-        file = os.path.join(os.getcwd(), file)
-        process.append(Process(target=_start, args=(object.Executable, ['--inifile', file])))
+        inifile = os.path.join(os.getcwd(), inifile)
+        
+        # pass only the specific ini file
+        args['inifile'] = inifile
+        
+        # pass all other arguments
+        args_to_pass = []
+        for k,v in args.items():
+            # reformat them back into command-line arguments
+            args_to_pass += ['--' + k.replace('_', '-'), v]
+        
+        process.append(Process(target=_start, args=(object.Executable, args_to_pass)))
 
     # see https://stackoverflow.com/questions/64174552/what-does-the-process-has-forked-and-you-cannot-use-this-corefoundation-functio
     if sys.version_info >= (3,0):
diff --git a/doc/scripts.md b/doc/scripts.md
index 68756224..1685b8ca 100644
--- a/doc/scripts.md
+++ b/doc/scripts.md
@@ -52,11 +52,8 @@ Each script is prefaced with a commented text explaining it's functionality, as
 The EEGsynth uses standard Python libraries as well as it's own, located in the `eegsynth/lib` directory:
 
 ```
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 from scipy import signal
@@ -64,12 +61,19 @@ from scipy import signal
 if hasattr(sys, 'frozen'):
     path = os.path.split(sys.executable)[0]
     file = os.path.split(sys.executable)[-1]
-elif sys.argv[0] != '':
+    name = os.path.splitext(file)[0]
+elif __name__=='__main__' and sys.argv[0] != '':
     path = os.path.split(sys.argv[0])[0]
     file = os.path.split(sys.argv[0])[-1]
-else:
+    name = os.path.splitext(file)[0]
+elif __name__=='__main__':
     path = os.path.abspath('')
     file = os.path.split(path)[-1] + '.py'
+    name = os.path.splitext(file)[0]
+else:
+    path = os.path.split(__file__)[0]
+    file = os.path.split(__file__)[-1]
+    name = os.path.splitext(file)[0]
 
 # eegsynth/lib contains shared modules
 sys.path.insert(0, os.path.join(path,'../../lib'))
@@ -93,25 +97,13 @@ We try to permit _all_ dynamic values to be set dynamically like this, which is
 done using the _patch object_:
 
 ```
-parser = argparse.ArgumentParser()
-parser.add_argument("-i", "--inifile", default=os.path.join(path, os.path.splitext(file)[0] + '.ini'), help="optional name of the configuration file")
-args = parser.parse_args()
 
-config = configparser.ConfigParser()
-config.read(args.inifile)
-
-try:
-    r = redis.StrictRedis(host=config.get('redis','hostname'), port=config.getint('redis','port'), db=0)
-    response = r.client_list()
-except redis.ConnectionError:
-    raise RuntimeError("'cannot connect to Redis server")
-
-# combine the patching from the configuration file and Redis
-patch = EEGsynth.patch(config, r)
-del config
+# configure and start the patch, this will parse the command-line arguments and the ini file
+# the default for <somemodule.py> is to read <somemodule.ini> in the same directory
+patch = EEGsynth.patch(name=name, path=path)
 
 # this determines how much debugging information gets printed
-debug = patch.getint('general','debug')
+debug = patch.getint('general', 'debug')
 
 # the scale and offset are used to map Redis values to signal parameters
 scale_frequency   = patch.getfloat('scale', 'frequency', default=1)
@@ -138,7 +130,7 @@ MIDI devices can be accessed in de code using the EEGsynth library:
 ```
 mididevice = patch.getstring('midi', 'device')
 try:
-    outputport  = mido.open_output(mididevice)
+    outputport = mido.open_output(mididevice)
     if debug>0:
         print("Connected to MIDI output")
 except:
@@ -176,6 +168,7 @@ try:
 
 except:
     raise RuntimeError("cannot connect to FieldTrip buffer")
+```
 
 
 To wait until there is _any_ data we can wait until the data header is available:
@@ -271,7 +264,7 @@ while True:
 
 In many cases, a module will output its results by changing some value(s) in Redis.
 Other modules can then read those values and use them to influence [output devices](output.md).
-Setting a value in Redis using the _patch obect_ is as simple as:
+Setting a value in Redis using the _patch() obect_ is as simple as:
 
 ```
 patch.setvalue(key, value)
@@ -335,7 +328,6 @@ Followed by the main loop, and a neat closing of thread when it's interrupted:
 
 ```
 while True:
-
     <MAIN LOOP>
 
 except (KeyboardInterrupt, RuntimeError) as e:
diff --git a/lib/DummyRedis.py b/lib/DummyRedis.py
new file mode 100644
index 00000000..b3da7d82
--- /dev/null
+++ b/lib/DummyRedis.py
@@ -0,0 +1,52 @@
+# This software is part of the EEGsynth project, see <https://github.com/eegsynth/eegsynth>.
+#
+# Copyright (C) 2023 EEGsynth project
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import time
+
+
+###################################################################################################
+class client():
+    def __init__(self):
+        return
+
+    def set(self, key, val):
+        return 'OK'
+
+    def get(self, key):
+        return None
+
+    def publish(self, key, val):
+        return 'OK'
+
+    def pubsub(self):
+        return pubsub()
+
+    def exists(self, key):
+        return False
+
+
+###################################################################################################
+class pubsub():
+    def __init__(self):
+        pass
+
+    def subscribe(self, channel):
+        pass
+
+    def listen(self):
+        time.sleep(1)
+        return []
diff --git a/lib/EEGsynth.py b/lib/EEGsynth.py
index 28e9672f..c2a2a381 100644
--- a/lib/EEGsynth.py
+++ b/lib/EEGsynth.py
@@ -1,6 +1,33 @@
+# This software is part of the EEGsynth project, see <https://github.com/eegsynth/eegsynth>.
+#
+# Copyright (C) 2023 EEGsynth project
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 from __future__ import print_function
 
+import os
 import sys
+
+# exclude the eegsynth/module/redis directory from the path
+for i, dir in enumerate(sys.path):
+    if dir.endswith(os.path.join('module', 'redis')):
+        del sys.path[i]
+        continue
+
+import configparser
+import argparse
 import time
 import threading
 import math
@@ -9,72 +36,343 @@
 import logging
 from logging import Formatter
 import colorama
+import random
+import redis          # this is NOT eegsynth/module/redis
+import ZmqRedis       # this offers an alternative to a real redis server
+import FakeRedis      # this offers an alternative to a real redis server
+import DummyRedis     # this offers an alternative to a real redis server
+import string
 import termcolor
 from termcolor import colored
 
 ###################################################################################################
-def formatkeyval(key, val):
-    if sys.version_info < (3,0):
-        # this works in Python 2, but fails in Python 3
-        isstring = isinstance(val, basestring)
-    else:
-        # this works in Python 3, but fails for unicode strings in Python 2
-        isstring = isinstance(val, str)
-    if val is None:
-        output = "%s = None" % (key)
-    elif isinstance(val, list):
-        output = "%s = %s" % (key, str(val))
-    elif isstring:
-        output = "%s = %s" % (key, val)
-    else:
-        output = "%s = %g" % (key, val)
-    return output
+class patch():
+    """Class to provide a generalized interface for patching modules using
+    command-line arguments, configuration files and Redis.
+    
+    The patch is initialized like this
+      patch = EEGsynth.patch(name=<name>, path=<path>)
+    where the name and path point to the ini file. You can also
+    pass the --inifile option on the command-line.
+
+    The following methods get the values (as a string) from the commnand-line
+    arguments or from the ini file
+      patch.get(section, item, default=None)
+
+    The following methods get the values either from the commnand-line
+    arguments, from the ini file or from Redis
+      patch.getfloat(section, item, multiple=False, default=None)
+      patch.getint(section, item, multiple=False, default=None)
+      patch.getstring(section, item, multiple=False, default=None)
+
+    The formatting of options on the command-line should be like this
+      --section-item value
+
+    The formatting of options in the ini file should be like this
+      [section]
+      item=1            this returns 1
+      item=key          get the value of the key from Redis
+    or to return multiple items the formatting should be like this
+      [section]
+      item=1-20         this returns [1,20]
+      item=1,2,3        this returns [1,2,3]
+      item=1,2,3,5-9    this returns [1,2,3,5,9], not [1,2,3,5,6,7,8,9]
+      item=key1,key2    get the value of key1 and key2 from Redis
+      item=key1,5       get the value of key1 from Redis
+      item=0,key2       get the value of key2 from Redis
+    """
 
-###################################################################################################
-def trimquotes(option):
-    # remove leading and trailing quotation marks
-    # this is needed to include leading or trailing spaces in an ini-file option
-    if option[0]=='"':
-        option = option[1:]
-    if option[0]=='\'':
-        option = option[1:]
-    if option[-1]=='"':
-        option = option[0:-1]
-    if option[-1]=='\'':
-        option = option[0:-1]
-    return option
+    def __init__(self, name=None, path=None, preservecase=False):
 
-###################################################################################################
-class ColoredFormatter(Formatter):
-    """Class to format logging messages
-    """
+        if not name==None and not path==None:
+            inifile = os.path.join(path, name + '.ini')
+        elif not name==None:
+            inifile = name + '.ini'
+        else:
+            inifile = None
+
+        parser = argparse.ArgumentParser()
+        parser.add_argument("-i", "--inifile", default=inifile, help="name of the configuration file")
+        parser.add_argument("--general-broker", default=None, help="general broker")
+        parser.add_argument("--general-debug", default=None, help="general debug")
+        parser.add_argument("--general-delay", default=None, help="general delay")
+        args = parser.parse_args()
+        
+        config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
+        if preservecase:
+            # see https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.optionxform
+            config.optionxform = str
+        if 'inifile' in args and not args.inifile==None:
+            config.read(args.inifile)
+        
+        # convert the command-line arguments in a dict
+        args = vars(args)
+        # remove empty items
+        args = {k:v for k,v in args.items() if v}
+        
+        if 'general_broker' in args:
+            broker = args['general_broker']
+        elif config.has_option('general', 'broker'):
+            broker = config.get('general', 'broker')
+        else:
+            # set the default broker
+            if config.has_section('redis'):
+                broker = 'redis'
+            elif config.has_section('zeromq'):
+                broker = 'zeromq'
+            else:
+                broker = 'dummy'
+                
+        if broker=='redis':
+            if config.has_option('redis', 'hostname'):
+                hostname = config.get('redis', 'hostname')
+            else:
+                hostname = 'localhost'
+            if config.has_option('redis', 'port'):
+                port = config.getint('redis', 'port')
+            else:
+                port = 6379
+            try:
+                r = redis.StrictRedis(host=hostname, port=port, db=0, charset='utf-8', decode_responses=True)
+                response = r.client_list()
+            except redis.ConnectionError:
+                raise RuntimeError("cannot connect to Redis server")
+
+        elif broker=='zeromq':
+            # make a connection to the specified ZeroMQ server
+            # the combination of server and client emulate a subset of redis functionality
+            if config.has_option('zeromq', 'hostname'):
+                hostname = config.get('zeromq', 'hostname')
+            else:
+                hostname = 'localhost'
+            if config.has_option('zeromq', 'port'):
+                port = config.getint('zeromq', 'port')
+            else:
+                port = 5555
+            r = ZmqRedis.client(host=hostname, port=port)
 
-    def __init__(self, name=None):
-        self.name = name
-        colors = list(termcolor.COLORS.keys()) # prevent RuntimeError: dictionary changed size during iteration
-        # add reverse and bright color
-        for color in colors:
-            termcolor.COLORS['reverse_'+color] = termcolor.COLORS[color]+10
-            termcolor.COLORS['bright_'+color] = termcolor.COLORS[color]+60
-        # for color in termcolor.COLORS.keys():
-        #     print(colored(color, color))
+        elif broker=='fake':
+            # the fake client stores all values in a dictionary
+            r = DummyRedis.client()
+
+        elif broker=='dummy':
+            # the dummy client has all functions but does not do anything
+            r = DummyRedis.client()
 
-    def format(self, record):
-        colors = {
-            'CRITICAL': 'reverse_red',
-            'ERROR': 'red',
-            'WARNING': 'yellow',
-            'SUCCESS': 'green',
-            'INFO': 'cyan',
-            'DEBUG': 'bright_grey',
-            'TRACE': 'reverse_white',
-        }
-        color = colors.get(record.levelname, 'white')
-        if self.name:
-            return colored(record.levelname, color) + ': ' + self.name + ': ' + record.getMessage()
         else:
-            return colored(record.levelname, color) + ': ' + record.getMessage()
+            raise RuntimeError("unknown broker")
+
+        # store the command-line arguments, the configuration object that maps the ini file, and the Redis connection
+        self.args = args
+        self.config = config
+        self.redis = r
+
+    ####################################################################
+    def pubsub(self):
+        return self.redis.pubsub()
+
+    ####################################################################
+    def publish(self, channel, value):
+        return self.redis.publish(channel, value)
+
+    ####################################################################
+    def get(self, section, item, default=None):
+        if section + "_" + item in self.args:
+            return self.args[section + "_" + item]
+        elif self.config.has_option(section, item):
+            return self.config.get(section, item)
+        else:
+            return default
+
+    ####################################################################
+    def getfloat(self, section, item, multiple=False, default=None):
+        if section + "_" + item in self.args:
+            # get it from the command-line arguments
+            return float(self.args[section + "_" + item])
+        elif self.config.has_option(section, item) and len(self.config.get(section, item))>0:
+            # get all items from the ini file, there might be one or multiple
+            items = self.config.get(section, item)
+
+            if multiple:
+                # convert the items to a list
+                if items.find(",") > -1:
+                    separator = ","
+                elif items.find("-") > -1:
+                    separator = "-"
+                elif items.find("\t") > -1:
+                    separator = "\t"
+                else:
+                    separator = " "
+                items = squeeze(' ', items)        # remove excess whitespace
+                items = squeeze(separator, items)  # remove double separators
+                items = items.split(separator)     # split on the separator
+            else:
+                # make a list with a single item
+                items = [items]
+
+            # set the default
+            if multiple and isinstance(default, list):
+                val = [float(x) for x in default]
+            elif default != None:
+                val = [float(default)] * len(items)
+            else:
+                val = [default] * len(items)
+
+            # convert the strings into floating point values
+            for i,item in enumerate(items):
+                try:
+                    # if it resembles a value, use that
+                    val[i] = float(item)
+                except ValueError:
+                    # if it is a string, get the value from Redis
+                    try:
+                        val[i] = float(self.redis.get(item))
+                    except TypeError:
+                        pass
+        else:
+            # the configuration file does not contain the item
+            if multiple and isinstance(default, list):
+                val = [float(x) for x in default]
+            elif multiple and default == None:
+                val = []
+            elif multiple and default != None:
+                val = [float(default)]
+            elif not multiple and default == None:
+                val = default
+            elif not multiple and default != None:
+                val = float(default)
+
+        if multiple and not isinstance(val, list):
+            # return a list
+            return [val]
+        elif not multiple and isinstance(val, list):
+            # return a single value
+            return val[0]
+        else:
+            return val
 
+    ####################################################################
+    def getint(self, section, item, multiple=False, default=None):
+        if section + "_" + item in self.args:
+            return int(self.args[section + "_" + item])
+        elif self.config.has_option(section, item) and len(self.config.get(section, item))>0:
+            # get all items from the ini file, there might be one or multiple
+            items = self.config.get(section, item)
+
+            if multiple:
+                # convert the items to a list
+                if items.find(",") > -1:
+                    separator = ","
+                elif items.find("-") > -1:
+                    separator = "-"
+                elif items.find("\t") > -1:
+                    separator = "\t"
+                else:
+                    separator = " "
+                items = squeeze(' ', items)        # remove excess whitespace
+                items = squeeze(separator, items)  # remove double separators
+                items = items.split(separator)     # split on the separator
+            else:
+                # make a list with a single item
+                items = [items]
+
+            # set the default
+            if multiple and isinstance(default, list):
+                val = [int(x) for x in default]
+            elif default != None:
+                val = [int(default)] * len(items)
+            else:
+                val = [default] * len(items)
+
+            # convert the strings into integer values
+            for i,item in enumerate(items):
+                try:
+                    # if it resembles a value, use that
+                    val[i] = int(item)
+                except ValueError:
+                    # if it is a string, get the value from Redis
+                    try:
+                        val[i] = int(round(float(self.redis.get(item))))
+                    except TypeError:
+                        pass
+        else:
+            # the configuration file does not contain the item
+            if multiple and isinstance(default, list):
+                val = [int(x) for x in default]
+            elif multiple and default == None:
+                val = []
+            elif multiple and default != None:
+                val = [int(default)]
+            elif not multiple and default == None:
+                val = default
+            elif not multiple and default != None:
+                val = int(default)
+
+        if multiple and not isinstance(val, list):
+            # return a list
+            return [val]
+        elif not multiple and isinstance(val, list):
+            # return a single value
+            return val[0]
+        else:
+            return val
+
+    ####################################################################
+    def getstring(self, section, item, multiple=False, default=None):
+        if section + "_" + item in self.args:
+            return self.args[section + "_" + item]
+        else:
+            # get all items from the ini file, there might be one or multiple
+            try:
+                val = self.config.get(section, item)
+                if self.redis.exists(val):
+                    # the ini file points to a Redis key, which contains the actual value
+                    val = self.redis.get(val)
+            except:
+                val = default
+
+        if multiple:
+            if val==None or len(val)==0:
+                # convert it to an empty list
+                val = []
+            else:
+                # convert the string with items to a list
+                if val.find(",") > -1:
+                    separator = ","
+                elif val.find("-") > -1:
+                    separator = "-"
+                elif val.find("\t") > -1:
+                    separator = "\t"
+                else:
+                    separator = " "
+
+                val = squeeze(separator, val)  # remove double separators
+                val = val.split(separator)     # split on the separator
+
+        if multiple and not isinstance(val, list):
+            # return a list
+            return [val]
+        elif not multiple and isinstance(val, list):
+            # return a single value
+            return val[0]
+        else:
+            return val
+
+    ####################################################################
+    def hasitem(self, section, item):
+        # check whether an item is present on the command line or in the ini file
+        if section + "_" + item in self.args:
+            return True
+        else:
+            return self.config.has_option(section, item)
+
+    ####################################################################
+    def setvalue(self, item, val, duration=0):
+        self.redis.set(item, val)      # set it as control channel
+        self.redis.publish(item, val)  # send it as trigger
+        if duration > 0:
+            # switch off after a certain amount of time
+            threading.Timer(duration, self.setvalue, args=[item, 0.]).start()
 
 ###################################################################################################
 class monitor():
@@ -135,7 +433,7 @@ def __init__(self, name=None, debug=0):
 ##############################################################################
 # %s is part of EEGsynth, see <http://www.eegsynth.org>.
 #
-# Copyright (C) 2017-2022 EEGsynth project
+# Copyright (C) 2017-2023 EEGsynth project
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -231,200 +529,35 @@ def trace(self, *args):
             self.logger.log(logging.TRACE, " ".join(map(format, args)))
 
 ###################################################################################################
-class patch():
-    """Class to provide a generalized interface for patching modules using
-    configuration files and Redis.
-
-    The formatting of the item in the ini file should be like this
-      item=1            this returns 1
-      item=key          get the value of the key from Redis
-    or if multiple is True
-      item=1-20         this returns [1,20]
-      item=1,2,3        this returns [1,2,3]
-      item=1,2,3,5-9    this returns [1,2,3,5,9], not [1,2,3,4,5,6,7,8,9]
-      item=key1,key2    get the value of key1 and key2 from Redis
-      item=key1,5       get the value of key1 from Redis
-      item=0,key2       get the value of key2 from Redis
+class ColoredFormatter(Formatter):
+    """Class to format logging messages
     """
 
-    def __init__(self, c, r):
-        self.config = c
-        self.redis  = r
-
-    ####################################################################
-    def getfloat(self, section, item, multiple=False, default=None):
-        if self.config.has_option(section, item) and len(self.config.get(section, item))>0:
-            # get all items from the ini file, there might be one or multiple
-            items = self.config.get(section, item)
-
-            if multiple:
-                # convert the items to a list
-                if items.find(",") > -1:
-                    separator = ","
-                elif items.find("-") > -1:
-                    separator = "-"
-                elif items.find("\t") > -1:
-                    separator = "\t"
-                else:
-                    separator = " "
-                items = squeeze(' ', items)        # remove excess whitespace
-                items = squeeze(separator, items)  # remove double separators
-                items = items.split(separator)     # split on the separator
-            else:
-                # make a list with a single item
-                items = [items]
-
-            # set the default
-            if default != None:
-                val = [float(default)] * len(items)
-            else:
-                val = [default] * len(items)
-
-            # convert the strings into floating point values
-            for i,item in enumerate(items):
-                try:
-                    # if it resembles a value, use that
-                    val[i] = float(item)
-                except ValueError:
-                    # if it is a string, get the value from Redis
-                    try:
-                        val[i] = float(self.redis.get(item))
-                    except TypeError:
-                        pass
-        else:
-            # the configuration file does not contain the item
-            if multiple == True and default == None:
-                val = []
-            elif multiple == True and default != None:
-                val = [float(x) for x in default]
-            elif multiple == False and default == None:
-                val = None
-            elif multiple == False and default != None:
-                val = float(default)
-
-        if multiple:
-            # return it as list
-            return val
-        else:
-            # return a single value
-            if isinstance(val, list):
-                return val[0]
-            else:
-                return val
-
-    ####################################################################
-    def getint(self, section, item, multiple=False, default=None):
-        if self.config.has_option(section, item) and len(self.config.get(section, item))>0:
-            # get all items from the ini file, there might be one or multiple
-            items = self.config.get(section, item)
-
-            if multiple:
-                # convert the items to a list
-                if items.find(",") > -1:
-                    separator = ","
-                elif items.find("-") > -1:
-                    separator = "-"
-                elif items.find("\t") > -1:
-                    separator = "\t"
-                else:
-                    separator = " "
-                items = squeeze(' ', items)        # remove excess whitespace
-                items = squeeze(separator, items)  # remove double separators
-                items = items.split(separator)     # split on the separator
-            else:
-                # make a list with a single item
-                items = [items]
-
-            # set the default
-            if default != None:
-                val = [int(default)] * len(items)
-            else:
-                val = [default] * len(items)
-
-            # convert the strings into integer values
-            for i,item in enumerate(items):
-                try:
-                    # if it resembles a value, use that
-                    val[i] = int(item)
-                except ValueError:
-                    # if it is a string, get the value from Redis
-                    try:
-                        val[i] = int(round(float(self.redis.get(item))))
-                    except TypeError:
-                        pass
-        else:
-            # the configuration file does not contain the item
-            if multiple == True and default == None:
-                val = []
-            elif multiple == True and default != None:
-                val = [int(x) for x in default]
-            elif multiple == False and default == None:
-                val = None
-            elif multiple == False and default != None:
-                val = int(default)
-
-        if multiple:
-            # return it as list
-            return val
-        else:
-            # return a single value
-            if isinstance(val, list):
-                return val[0]
-            else:
-                return val
-
-    ####################################################################
-    def getstring(self, section, item, default=None, multiple=False):
-        # get all items from the ini file, there might be one or multiple
-        try:
-            val = self.config.get(section, item)
-            if self.redis.exists(val):
-                # the ini file points to a Redis key, which contains the actual value
-                val = self.redis.get(val)
-        except:
-            val = default
-
-        if multiple:
-            if val==None or len(val)==0:
-                # convert it to an empty list
-                val = []
-            else:
-                # convert the string with items to a list
-                if val.find(",") > -1:
-                    separator = ","
-                elif val.find("-") > -1:
-                    separator = "-"
-                elif val.find("\t") > -1:
-                    separator = "\t"
-                else:
-                    separator = " "
-
-                val = squeeze(separator, val)  # remove double separators
-                val = val.split(separator)     # split on the separator
+    def __init__(self, name=None):
+        self.name = name
+        colors = list(termcolor.COLORS.keys()) # prevent RuntimeError: dictionary changed size during iteration
+        # add reverse and bright color
+        for color in colors:
+            termcolor.COLORS['reverse_'+color] = termcolor.COLORS[color]+10
+            termcolor.COLORS['bright_'+color] = termcolor.COLORS[color]+60
+        # for color in termcolor.COLORS.keys():
+        #     print(colored(color, color))
 
-        if multiple:
-            # return it as list
-            return val
+    def format(self, record):
+        colors = {
+            'CRITICAL': 'reverse_red',
+            'ERROR': 'red',
+            'WARNING': 'yellow',
+            'SUCCESS': 'green',
+            'INFO': 'cyan',
+            'DEBUG': 'bright_grey',
+            'TRACE': 'reverse_white',
+        }
+        color = colors.get(record.levelname, 'white')
+        if self.name:
+            return colored(record.levelname, color) + ': ' + self.name + ': ' + record.getMessage()
         else:
-            # return a single value
-            if isinstance(val, list):
-                return val[0]
-            else:
-                return val
-
-    ####################################################################
-    def hasitem(self, section, item):
-        # check whether an item is present in the ini file
-        return self.config.has_option(section, item)
-
-    ####################################################################
-    def setvalue(self, item, val, duration=0):
-        self.redis.set(item, val)      # set it as control channel
-        self.redis.publish(item, val)  # send it as trigger
-        if duration > 0:
-            # switch off after a certain amount of time
-            threading.Timer(duration, self.setvalue, args=[item, 0.]).start()
-
+            return colored(record.levelname, color) + ': ' + record.getMessage()
 
 ####################################################################
 def rescale(xval, slope=None, offset=None, reverse=False):
@@ -732,3 +865,39 @@ def notch_filter(dat, f0, fs, Q=30, dir='onepass'):
         # average
         y = (y1 + y2)/2
     return y
+
+###################################################################################################
+def formatkeyval(key, val):
+    if sys.version_info < (3,0):
+        # this works in Python 2, but fails in Python 3
+        isstring = isinstance(val, basestring)
+    else:
+        # this works in Python 3, but fails for unicode strings in Python 2
+        isstring = isinstance(val, str)
+    if val is None:
+        output = "%s = None" % (key)
+    elif isinstance(val, list):
+        output = "%s = %s" % (key, str(val))
+    elif isstring:
+        output = "%s = %s" % (key, val)
+    else:
+        output = "%s = %g" % (key, val)
+    return output
+
+###################################################################################################
+def trimquotes(option):
+    # remove leading and trailing quotation marks
+    # this is needed to include leading or trailing spaces in an ini-file option
+    if option[0]=='"':
+        option = option[1:]
+    if option[0]=='\'':
+        option = option[1:]
+    if option[-1]=='"':
+        option = option[0:-1]
+    if option[-1]=='\'':
+        option = option[0:-1]
+    return option
+
+###################################################################################################
+def uuid(length):
+    return ''.join(random.choice(string.hexdigits) for i in range(length))
diff --git a/lib/FakeRedis.py b/lib/FakeRedis.py
new file mode 100644
index 00000000..4f5dd3d9
--- /dev/null
+++ b/lib/FakeRedis.py
@@ -0,0 +1,70 @@
+# This software is part of the EEGsynth project, see <https://github.com/eegsynth/eegsynth>.
+#
+# Copyright (C) 2023 EEGsynth project
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import threading
+import time
+
+store = {}
+latest = None
+
+
+###################################################################################################
+class client():
+    def __init__(self):
+        store = {}
+
+    def set(self, key, val):
+        global store, latest
+        store[key] = val
+        return
+
+    def get(self, key):
+        global store, latest
+        if key in store:
+            return store[key]
+        else:
+            return None
+
+    def publish(self, key, val):
+        global store, latest
+        store[key] = val
+        latest = key
+
+    def pubsub(self):
+        return pubsub()
+
+    def exists(self, key):
+        return key in store
+
+###################################################################################################
+class pubsub():
+    def __init__(self):
+        self.subscribed = []
+
+    def subscribe(self, key):
+        self.subscribed.append(key)
+
+    def listen(self):
+        global store, latest
+        while not latest in self.subscribed:
+            time.sleep(0.1)
+        item = dict()
+        item['type'] = 'message'
+        item['channel'] = latest
+        item['data'] = store[latest]
+        latest = None
+        return [item]
diff --git a/lib/ZmqRedis.py b/lib/ZmqRedis.py
new file mode 100644
index 00000000..30059d4d
--- /dev/null
+++ b/lib/ZmqRedis.py
@@ -0,0 +1,137 @@
+# This software is part of the EEGsynth project, see <https://github.com/eegsynth/eegsynth>.
+#
+# Copyright (C) 2023 EEGsynth project
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import zmq
+import threading
+
+###################################################################################################
+class server():
+    def __init__(self, port=5555):
+        context = zmq.Context()
+        command = context.socket(zmq.REP)
+        command.bind("tcp://*:%d" % (port+0))  # this is the socket for most commands
+        publish = context.socket(zmq.PUB)
+        publish.bind("tcp://*:%d" % (port+1))  # this is the socket for publishing
+        self.command = command
+        self.publish = publish
+        self.store = {}
+        self.debug = 0
+
+    def start(self):
+        while True:
+            message = self.command.recv_string()
+            cmd, key = message.split(' ', 1)
+            if cmd == 'SET':
+                if self.debug>1:
+                    print(message)
+                key, val = key.split(' ', 1)
+                self.store[key] = val
+                self.command.send_string('OK')
+            elif cmd == 'GET':
+                if self.debug>1:
+                    print(message)
+                if key in self.store:
+                    self.command.send_string(self.store[key])
+                else:
+                    self.command.send_string('')
+            elif cmd == 'PUBLISH':
+                if self.debug>0:
+                    print(message)
+                key, val = key.split(' ', 1)
+                self.command.send_string('OK')
+                self.publish.send_string('%s %s' % (key, val))
+            elif cmd == 'EXISTS':
+                if self.debug>2:
+                    print(message)
+                if key in self.store.keys():
+                    self.command.send_string('1')
+                else:
+                    self.command.send_string('0')
+
+
+###################################################################################################
+class client():
+    def __init__(self, host='localhost', port=5555):
+        context = zmq.Context()
+        socket = context.socket(zmq.REQ)
+        socket.connect("tcp://%s:%d" % (host, port))
+        self.socket = socket
+        self.debug = 0
+        self.lock = threading.Lock()
+
+    def pubsub(self):
+        return pubsub()
+
+    def set(self, key, val):
+        if self.debug>0:
+            print("SET %s %f" % (key, val))
+        with self.lock:
+            self.socket.send_string("SET %s %f" % (key, val))
+            status = self.socket.recv_string()
+        return
+        
+    def get(self, key):
+        if self.debug>0:
+            print("GET %s" % key)
+        with self.lock:
+            self.socket.send_string("GET %s" % key)
+            val = self.socket.recv_string()
+        if len(val)==0:
+            return None
+        else:
+            return val
+
+    def publish(self, key, val):
+        if self.debug>0:
+            print("PUBLISH %s %f" % (key, val))
+        with self.lock:
+            self.socket.send_string("PUBLISH %s %f" % (key, val))
+            status = self.socket.recv_string()
+        return status
+
+    def exists(self, key):
+        with self.lock:
+            self.socket.send_string("EXISTS %s" % key)
+            val = bool(float(self.socket.recv_string()))
+        return val
+        
+
+###################################################################################################
+class pubsub():
+    def __init__(self):
+        context = zmq.Context()
+        socket = context.socket(zmq.SUB)
+        socket.connect("tcp://localhost:5556")
+        self.socket = socket
+
+    def subscribe(self, channel):
+        self.socket.setsockopt_string(zmq.SUBSCRIBE, channel)
+
+    def listen(self):
+        message = self.socket.recv_string()
+        key, val = message.split()
+        item = {}
+        item['type'] = 'message'
+        item['channel'] = key
+        item['data'] = val
+        return [item]
+
+
+###################################################################################################
+if __name__ == "__main__":
+    r = server()
+    r.start()
diff --git a/module/accelerometer/accelerometer.py b/module/accelerometer/accelerometer.py
index 3b0e9a0e..dfdc478f 100755
--- a/module/accelerometer/accelerometer.py
+++ b/module/accelerometer/accelerometer.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -54,26 +51,21 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError('cannot connect to Redis server')
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, window, channel_items, channel_name, channel_indx, item, begsample, endsample
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -85,14 +77,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to FieldTrip buffer")
 
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, window, channel_items, channel_name, channel_indx, item, begsample, endsample
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
@@ -113,7 +97,7 @@ def _start():
     window = patch.getfloat('processing', 'window')
     window = int(window * hdr_input.fSample)  # expressed in samples
 
-    channel_items = config.items('input')
+    channel_items = patch.config.items('input')
     channel_name = []
     channel_indx = []
     for item in channel_items:
@@ -129,8 +113,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, window, channel_items, channel_name, channel_indx, item, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, window, channel_items, channel_name, channel_indx, item, begsample, endsample
     global dat, channame, chanindx, key, val
 
     hdr_input = ft_input.getHeader()
diff --git a/module/audio2ft/audio2ft.py b/module/audio2ft/audio2ft.py
index b0f5adb4..39533d11 100755
--- a/module/audio2ft/audio2ft.py
+++ b/module/audio2ft/audio2ft.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import pyaudio
@@ -55,34 +52,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument(
-        "-i",
-        "--inifile",
-        default=os.path.join(path, name + ".ini"),
-        help="name of the configuration file",
-    )
-    args = parser.parse_args()
+    global patch, name, path, monitor
 
-    config = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
-    config.read(args.inifile)
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    try:
-        r = redis.StrictRedis(
-            host=config.get("redis", "hostname"),
-            port=config.getint("redis", "port"),
-            db=0,
-            charset="utf-8",
-            decode_responses=True,
-        )
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug", default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -93,18 +69,8 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, device, rate, blocksize, nchans, ft_host, ft_port, ft_output, p, info, i, devinfo, stream, startfeedback, countfeedback
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug"))
-
-    # get the options from the configuration file
-    debug = patch.getint("general", "debug")
-    device = patch.getint("audio", "device")
-    rate = patch.getint("audio", "rate", default=44100)
-    blocksize = patch.getint("audio", "blocksize", default=1024)
-    nchans = patch.getint("audio", "nchans", default=2)
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, device, rate, blocksize, nchans, p, info, i, devinfo, stream, startfeedback, countfeedback
 
     try:
         ft_host = patch.getstring("fieldtrip", "hostname")
@@ -116,6 +82,12 @@ def _start():
     except:
         raise RuntimeError("cannot connect to output FieldTrip buffer")
 
+    # get the options from the configuration file
+    device = patch.getint("audio", "device")
+    rate = patch.getint("audio", "rate", default=44100)
+    blocksize = patch.getint("audio", "blocksize", default=1024)
+    nchans = patch.getint("audio", "nchans", default=2)
+
     monitor.info("rate = %g" % rate)
     monitor.info("nchans = %g" % nchans)
     monitor.info("blocksize = %g" % blocksize)
@@ -160,8 +132,8 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global startfeedback, countfeedback
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, device, rate, blocksize, nchans, p, info, i, devinfo, stream, startfeedback, countfeedback
     global start, data
 
     # measure the time that it takes
diff --git a/module/audiomixer/audiomixer.py b/module/audiomixer/audiomixer.py
index efca06fb..c501b07f 100755
--- a/module/audiomixer/audiomixer.py
+++ b/module/audiomixer/audiomixer.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import pyaudio
@@ -66,34 +63,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument(
-        "-i",
-        "--inifile",
-        default=os.path.join(path, name + ".ini"),
-        help="name of the configuration file",
-    )
-    args = parser.parse_args()
+    global patch, name, path, monitor
 
-    config = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
-    config.read(args.inifile)
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    try:
-        r = redis.StrictRedis(
-            host=config.get("redis", "hostname"),
-            port=config.getint("redis", "port"),
-            db=0,
-            charset="utf-8",
-            decode_responses=True,
-        )
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug", default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -104,14 +80,11 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, delay, p, input_device, output_device, rate, blocksize, input_nchans, output_nchans, input_stream, output_stream, mix, previous
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug"))
+    global patch, name, path, monitor
+    global debug, delay, p, input_device, output_device, rate, blocksize, input_nchans, output_nchans, input_stream, output_stream, mix, previous
 
     # get the options from the configuration file
-    debug = patch.getint("general", "debug")
+    debug = patch.getint("general", "debug", default=1)
     delay = patch.getfloat("general", "delay", default=0.05)
     input_device = patch.getint("input", "device")
     input_nchans = patch.getint("input", "nchans", default=2)
@@ -180,8 +153,8 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, debug, delay, blocksize, input_stream, output_stream, mix, previous
+    global patch, name, path, monitor
+    global debug, delay, blocksize, input_stream, output_stream, mix, previous
 
     now = time.time()
     if (now-previous)>delay :
diff --git a/module/biochill/biochill.py b/module/biochill/biochill.py
old mode 100644
new mode 100755
index faaf1935..ebe3c10b
--- a/module/biochill/biochill.py
+++ b/module/biochill/biochill.py
@@ -17,16 +17,12 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import numpy as np
 from scipy.signal import sosfilt_zi, sosfilt
 
-
 if hasattr(sys, 'frozen'):
     path = os.path.split(sys.executable)[0]
     file = os.path.split(sys.executable)[-1]
@@ -52,33 +48,13 @@
 
 
 class BreathingBiofeedback:
-
     def __init__(self, path, name):
 
-        # Configuration.
-        parser = argparse.ArgumentParser()
-        parser.add_argument("-i", "--inifile",
-                            default=os.path.join(path, name + '.ini'),
-                            help="name of the configuration file")
-        args = parser.parse_args()
-
-        config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-        config.read(args.inifile)
-
-        # Redis.
-        try:
-            rds = redis.StrictRedis(host=config.get('redis', 'hostname'),
-                                    port=config.getint('redis', 'port'),
-                                    db=0, charset='utf-8',
-                                    decode_responses=True)
-        except redis.ConnectionError as e:
-            raise RuntimeError(e)
-
-        self.patch = EEGsynth.patch(config, rds)    # combine patching from configuration file and Redis.
-
+        # configure and start the patch, this will parse the command-line arguments and the ini file
+        self.patch = EEGsynth.patch(name=name, path=path)
+        
         # Monitor.
-        self.monitor = EEGsynth.monitor(name=name,
-                                        debug=self.patch.getint('general', 'debug'))
+        self.monitor = EEGsynth.monitor(name=name, debug=self.patch.getint('general', 'debug', default=1))
 
         # FieldTrip.
         try:
diff --git a/module/bitalino2ft/bitalino2ft.py b/module/bitalino2ft/bitalino2ft.py
index e7beeaef..76dee28c 100755
--- a/module/bitalino2ft/bitalino2ft.py
+++ b/module/bitalino2ft/bitalino2ft.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 from scipy import signal as sp
@@ -56,34 +53,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument(
-        "-i",
-        "--inifile",
-        default=os.path.join(path, name + ".ini"),
-        help="name of the configuration file",
-    )
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(
-            host=config.get("redis", "hostname"),
-            port=config.getint("redis", "port"),
-            db=0,
-            charset="utf-8",
-            decode_responses=True,
-        )
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug", default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -94,14 +70,11 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global  monitor, debug, device, fsample, blocksize, channels, batterythreshold, nchans, startfeedback, countfeedback, ft_host, ft_port, ft_output, datatype, digitalOutput
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug"))
+    global patch, name, path, monitor
+    global debug, device, fsample, blocksize, channels, batterythreshold, nchans, startfeedback, countfeedback, ft_host, ft_port, ft_output, datatype, digitalOutput
 
     # get the options from the configuration file
-    debug = patch.getint("general", "debug")
+    debug = patch.getint("general", "debug", default=1)
     device = patch.getstring("bitalino", "device")
     fsample = patch.getfloat("bitalino", "fsample", default=1000)
     blocksize = patch.getint("bitalino", "blocksize", default=10)
@@ -160,8 +133,8 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, debug, device, fsample, blocksize, channels, batterythreshold, nchans, startfeedback, countfeedback, ft_host, ft_port, ft_output, datatype, digitalOutput
+    global patch, name, path, monitor
+    global debug, device, fsample, blocksize, channels, batterythreshold, nchans, startfeedback, countfeedback, ft_host, ft_port, ft_output, datatype, digitalOutput
     global start, dat
 
     # measure the time that it takes
diff --git a/module/brainflow2ft/brainflow2ft.py b/module/brainflow2ft/brainflow2ft.py
index 647c8671..31cdf504 100755
--- a/module/brainflow2ft/brainflow2ft.py
+++ b/module/brainflow2ft/brainflow2ft.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
-import redis
 import os
 import sys
 import time
@@ -57,34 +54,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument(
-        "-i",
-        "--inifile",
-        default=os.path.join(path, name + ".ini"),
-        help="name of the configuration file",
-    )
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(
-            host=config.get("redis", "hostname"),
-            port=config.getint("redis", "port"),
-            db=0,
-            charset="utf-8",
-            decode_responses=True,
-        )
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug", default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -95,15 +71,8 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, debug, delay, ft_host, ft_port, ft_output, board_id, streamer_params, params, board
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug"))
-
-    # get the general options from the configuration file
-    debug = patch.getint("general", "debug")
-    delay = patch.getfloat("general", "delay")
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, delay, board_id, streamer_params, params, board
 
     try:
         ft_host = patch.getstring("fieldtrip", "hostname")
@@ -115,6 +84,9 @@ def _start():
     except:
         raise RuntimeError("cannot connect to output FieldTrip buffer")
 
+    # get the general options from the configuration file
+    delay = patch.getfloat("general", "delay")
+
     # get the options that are specific for BrainFlow
     board_id             = patch.getint("brainflow", "board_id", default=-1)
     streamer_params      = patch.getstring("brainflow", "streamer_params", default=None)
@@ -157,8 +129,9 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, delay, ft_output, board, data
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, delay, board_id, streamer_params, params, board
+    global data
     
     if board.get_board_data_count()>0:
         data = np.transpose(board.get_board_data())
diff --git a/module/buffer/buffer.py b/module/buffer/buffer.py
index a032c2bd..fc7314c3 100755
--- a/module/buffer/buffer.py
+++ b/module/buffer/buffer.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import threading
@@ -54,37 +51,24 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
 
 def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, delay, port, server
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, delay, port, server
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     delay = patch.getfloat('general', 'delay', default=0.010)
     port = patch.getint('fieldtrip', 'port', multiple=True)
     
diff --git a/module/buffer/buffer.sh b/module/buffer/buffer.sh
index 2e2809aa..bd2c3fb2 100755
--- a/module/buffer/buffer.sh
+++ b/module/buffer/buffer.sh
@@ -4,7 +4,7 @@
 # in the specified or the default inifile.
 #
 # Use as
-#   buffer.sh [-i <inifile>] [-h] [-v]
+#   buffer.sh [-h] [-v] [-i <inifile>]
 
 # include library with helper functions
 . "$(dirname "$0")/../../lib/EEGsynth.sh"
@@ -17,19 +17,20 @@ BINDIR=$DIR/../../bin
 INIFILE=${DIR}/${NAME}.ini
 VERBOSE=0
 
-while getopts "hvi:" option; do
-  case "${option}" in
-    i)
-      INIFILE=${OPTARG}
+while getopts "hvi:" OPTION; do
+  case ${OPTION} in
+    h)
+      echo "Use as: $0 [-h] [-v] [-i <inifile>]"
+      exit 0
       ;;
     v)
       VERBOSE=1
       ;;
-    h)
-      echo "Use as: $0 [-i <inifile>] [-h] [-v]"
+    i)
+      INIFILE=${OPTARG}
       ;;
     \?)
-      echo "Invalid option: -${OPTARG}" >&2
+      exit 0
       ;;
   esac
 done
diff --git a/module/clockdivider/clockdivider.py b/module/clockdivider/clockdivider.py
index b6de493f..0156ea67 100755
--- a/module/clockdivider/clockdivider.py
+++ b/module/clockdivider/clockdivider.py
@@ -17,10 +17,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import threading
 import time
@@ -60,7 +57,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         global count
         # this message unblocks the Redis listen command
         pubsub.subscribe('CLOCKDIVIDER_UNBLOCK')
@@ -82,23 +79,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -109,14 +96,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, channels, dividers, count, triggers, channel, divider, thread
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, channels, dividers, count, triggers, channel, divider, thread
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     channels = patch.getstring('clock', 'channel', multiple=True)
     dividers = patch.getint('clock', 'rate', multiple=True)
 
@@ -142,8 +126,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, channels, dividers, count, triggers, channel, divider, thread
+    global patch, name, path, monitor
+    global debug, channels, dividers, count, triggers, channel, divider, thread
 
     monitor.update("count", count / len(dividers))
 
@@ -169,7 +153,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in triggers:
         thread.stop()
-    r.publish('CLOCKDIVIDER_UNBLOCK', 1)
+    patch.publish('CLOCKDIVIDER_UNBLOCK', 1)
     for thread in triggers:
         thread.join()
     sys.exit()
diff --git a/module/clockmultiplier/clockmultiplier.py b/module/clockmultiplier/clockmultiplier.py
index 35f694b5..6e0fd79c 100755
--- a/module/clockmultiplier/clockmultiplier.py
+++ b/module/clockmultiplier/clockmultiplier.py
@@ -17,10 +17,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import threading
 import time
@@ -69,7 +66,7 @@ def stop(self):
     def run(self):
         global count
         global interval
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         # this message unblocks the Redis listen command
         pubsub.subscribe('CLOCKMULTIPLIER_UNBLOCK')
         # this message triggers the event
@@ -118,23 +115,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -145,14 +132,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, channels, multipliers, lrate, count, triggers, channel, multiplier, thread
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+    global patch, name, path, monitor
+    global debug, channels, multipliers, lrate, count, triggers, channel, multiplier, thread
 
     # get the options from the configuration file
-    debug       = patch.getint('general', 'debug')
+    debug       = patch.getint('general', 'debug', default=1)
     channels    = patch.getstring('clock', 'channel', multiple=True)
     multipliers = patch.getint('clock', 'rate',  multiple=True)
     lrate       = patch.getfloat('clock', 'learning_rate', default=1)
@@ -179,8 +163,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, channels, multipliers, lrate, count, triggers, channel, multiplier, thread
+    global patch, name, path, monitor
+    global debug, channels, multipliers, lrate, count, triggers, channel, multiplier, thread
 
     monitor.update("count", count / len(multipliers))
 
@@ -207,7 +191,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in triggers:
         thread.stop()
-    r.publish('CLOCKMULTIPLIER_UNBLOCK', 1)
+    patch.publish('CLOCKMULTIPLIER_UNBLOCK', 1)
     for thread in triggers:
         thread.join()
     sys.exit()
diff --git a/module/cogito/cogito.py b/module/cogito/cogito.py
index 847a357f..f32cf023 100755
--- a/module/cogito/cogito.py
+++ b/module/cogito/cogito.py
@@ -21,13 +21,10 @@
 
 from __future__ import print_function
 
-import configparser
-import argparse
 import numpy as np
 import os
 import sys
 import time
-import redis
 from copy import copy
 
 # Pandas is a rather large Python package with a lot of extra dependencies.
@@ -61,29 +58,21 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
-
-    # get the options from the configuration file
-    debug   = patch.getint('general', 'debug')
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, ft_output, timeout, hdr_input, start, input_number, input_channel, output_number, output_channel, nInputs, number, channel, nOutputs, tmp, sample_rate, window, f_min, f_max, f_offset, scaling, polyorder, profileMin, profileMax, profileCorrection, layout, definition, val, positions, inputscaling, outputscaling, begsample, endsample
 
     try:
         ft_host = patch.getstring('input_fieldtrip', 'hostname')
@@ -105,14 +94,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to output FieldTrip buffer")
 
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output, name
-    global timeout, hdr_input, start, input_number, input_channel, output_number, output_channel, nInputs, number, channel, nOutputs, tmp, sample_rate, window, f_min, f_max, f_offset, scaling, polyorder, profileMin, profileMax, profileCorrection, layout, definition, val, positions, inputscaling, outputscaling, begsample, endsample
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('input_fieldtrip','timeout', default=30)
 
@@ -130,8 +111,8 @@ def _start():
     monitor.debug(hdr_input.labels)
 
     # get the input and output options
-    input_number, input_channel = list(map(list, list(zip(*config.items('input_channel')))))
-    output_number, output_channel = list(map(list, list(zip(*config.items('output_channel')))))
+    input_number, input_channel = list(map(list, list(zip(*patch.config.items('input_channel')))))
+    output_number, output_channel = list(map(list, list(zip(*patch.config.items('output_channel')))))
 
     # convert to integer and make the indices zero-offset
     input_number = [int(number)-1 for number in input_number]
@@ -161,13 +142,12 @@ def _start():
     output_number = list(range(nOutputs))
     output_channel = tmp
 
-    if debug > 0:
-        monitor.info('===== input channels =====')
-        for number, channel in zip(input_number, input_channel):
-            monitor.info(str(number) + ' = ' + channel)
-        monitor.info('===== output channels =====')
-        for number, channel in zip(output_number, output_channel):
-            monitor.info(str(number) + ' = ' + channel)
+    monitor.info('===== input channels =====')
+    for number, channel in zip(input_number, input_channel):
+        monitor.info(str(number) + ' = ' + channel)
+    monitor.info('===== output channels =====')
+    for number, channel in zip(output_number, output_channel):
+        monitor.info(str(number) + ' = ' + channel)
 
     sample_rate         = patch.getfloat('cogito', 'sample_rate')
     window              = patch.getfloat('cogito', 'window')
@@ -214,8 +194,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output
-    global timeout, hdr_input, start, input_number, input_channel, output_number, output_channel, nInputs, number, channel, nOutputs, tmp, sample_rate, window, f_min, f_max, f_offset, scaling, polyorder, profileMin, profileMax, profileCorrection, layout, definition, val, positions, inputscaling, outputscaling, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, ft_output, timeout, hdr_input, start, input_number, input_channel, output_number, output_channel, nInputs, number, channel, nOutputs, tmp, sample_rate, window, f_min, f_max, f_offset, scaling, polyorder, profileMin, profileMax, profileCorrection, layout, definition, val, positions, inputscaling, outputscaling, begsample, endsample
     global dat_input, tmpvar, ch, chan_time, original, t, fourier, mask, convert, signal_time, signal, dat_output, write_time
 
     # determine when we start polling for available data
diff --git a/module/complexity/complexity.py b/module/complexity/complexity.py
index e39e4b1a..acc48425 100755
--- a/module/complexity/complexity.py
+++ b/module/complexity/complexity.py
@@ -19,13 +19,10 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import math
 import multiprocessing
 import numpy as np
 import os
-import redis
 import sys
 import threading
 import time
@@ -61,33 +58,25 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
-
-    # get the options from the configuration file
-    debug = patch.getint('general','debug')
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel_items, channame, chanindx, item, shannon, sampen, multiscale, spectral, svd, correlation, higushi, petrosian, fisher, hurst, dfa, lyap_r, lyap_e, window, taper, begsample, endsample
 
     try:
-        ft_host = patch.getstring('fieldtrip','hostname')
-        ft_port = patch.getint('fieldtrip','port')
+        ft_host = patch.getstring('fieldtrip', 'hostname')
+        ft_port = patch.getint('fieldtrip', 'port')
         monitor.success('Trying to connect to buffer on %s:%i ...' % (ft_host, ft_port))
         ft_input = FieldTrip.Client()
         ft_input.connect(ft_host, ft_port)
@@ -95,14 +84,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to FieldTrip buffer")
 
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, channel_items, channame, chanindx, item, shannon, sampen, multiscale, spectral, svd, correlation, higushi, petrosian, fisher, hurst, dfa, lyap_r, lyap_e, window, taper, begsample, endsample
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
@@ -119,7 +100,7 @@ def _start():
     monitor.debug(hdr_input)
     monitor.debug(hdr_input.labels)
 
-    channel_items = config.items('input')
+    channel_items = patch.config.items('input')
     channame = []
     chanindx = []
     for item in channel_items:
@@ -157,8 +138,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, channel_items, channame, chanindx, item, shannon, sampen, multiscale, spectral, svd, correlation, higushi, petrosian, fisher, hurst, dfa, lyap_r, lyap_e, window, taper, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel_items, channame, chanindx, item, shannon, sampen, multiscale, spectral, svd, correlation, higushi, petrosian, fisher, hurst, dfa, lyap_r, lyap_e, window, taper, begsample, endsample
     global dat, meandat, chan, sample, metrics, timeseries, metric_names, metric, shortmetric, key, val
 
     hdr_input = ft_input.getHeader()
diff --git a/module/compressor/compressor.py b/module/compressor/compressor.py
index 88415191..84d11220 100755
--- a/module/compressor/compressor.py
+++ b/module/compressor/compressor.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 
@@ -52,23 +49,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -79,19 +66,16 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, delay, prefix, input_name, input_variable
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, delay, prefix, input_name, input_variable
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     delay = patch.getfloat('general', 'delay')
     prefix = patch.getstring('output', 'prefix')
 
     # get the input options
-    input_name, input_variable = list(zip(*config.items('input')))
+    input_name, input_variable = list(zip(*patch.config.items('input')))
 
     for name, variable in zip(input_name, input_variable):
         monitor.info("%s = %s" % (name, variable))
@@ -105,8 +89,8 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, debug, delay, prefix, input_name, input_variable
+    global patch, name, path, monitor
+    global debug, delay, prefix, input_name, input_variable
 
     if patch.getint('processing', 'enable', default=1):
         # the compressor/expander applies to all channels and must exist as float or redis key
diff --git a/module/csp/csp.py b/module/csp/csp.py
old mode 100644
new mode 100755
index 230555c4..cf20f8e2
--- a/module/csp/csp.py
+++ b/module/csp/csp.py
@@ -25,11 +25,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import wave
@@ -70,23 +67,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-i', '--inifile', default=os.path.join(path, name + '.ini'), help='name of the configuration file')
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError('cannot connect to Redis server')
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -97,7 +84,7 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, stepsize, filename, fileformat
     global data_A, filepath_A
     global data_B, filepath_B
@@ -105,9 +92,6 @@ def _start():
     global lpfreq, hpfreq, filtorder, hp, lp
     global filenames, indx, filenr, filename, f, data, chanindx, filters, cmap, fig, axes, i, ax, im, divider, cax, prefix, icomp, comp, iweight, weight, key
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     stepsize   = patch.getfloat('general', 'delay')
     filepath_A = patch.getstring('data', 'conditionA')
@@ -132,7 +116,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, H, A
     global D, stepsize
     global data_A, filepath_A
@@ -203,7 +187,7 @@ def _loop_once():
         raise NotImplementedError('unsupported file format')
 
     # plot timecourses for debugging
-    if patch.getint('general', 'debug') == 3:
+    if patch.getint('general', 'debug', default=1) == 3:
         for filenr, filename in enumerate(filenames):
             fig = plt.figure(filenr+1)
             for chanindx in range(nChannels[0]):
@@ -216,7 +200,7 @@ def _loop_once():
     filters = CSP(data[indx==0], data[indx==1])
 
     # plot CSP when debugging
-    if patch.getint('general', 'debug') == 3:
+    if patch.getint('general', 'debug', default=1) == 3:
         cmap = cm.coolwarm
         fig, axes = plt.subplots(1,2, figsize=(8,8))
         for i, ax in enumerate(axes.flat):
diff --git a/module/delaytrigger/delaytrigger.py b/module/delaytrigger/delaytrigger.py
index 723b47a2..f8d6a97e 100755
--- a/module/delaytrigger/delaytrigger.py
+++ b/module/delaytrigger/delaytrigger.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import threading
@@ -68,8 +65,8 @@ def stop(self):
         self.running = False
 
     def run(self):
-        global r, monitor, lock
-        pubsub = r.pubsub()
+        global patch, name, path, monitor, lock
+        pubsub = patch.pubsub()
         pubsub.subscribe('DELAYTRIGGER_UNBLOCK')   # this message unblocks the Redis listen command
         pubsub.subscribe(self.redischannel)        # this message triggers the event
         while self.running:
@@ -93,23 +90,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -120,19 +107,16 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, prefix, item, val, input_name, input_variable, output_name, output_variable, lock, trigger, thread
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # create the background threads that deal with the input triggers
     trigger = []
     monitor.debug("Setting up threads")
-    for item in config.items('input'):
+    for item in patch.config.items('input'):
         input = item[1]
         delay = patch.getfloat("delay", item[0])
         output = patch.getstring("output", item[0])
@@ -170,7 +154,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('DELAYTRIGGER_UNBLOCK', 1)
+    patch.publish('DELAYTRIGGER_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/demodulatetone/demodulatetone.py b/module/demodulatetone/demodulatetone.py
index 2bdd489f..c3062bda 100755
--- a/module/demodulatetone/demodulatetone.py
+++ b/module/demodulatetone/demodulatetone.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import pyaudio
@@ -55,34 +52,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument(
-        "-i",
-        "--inifile",
-        default=os.path.join(path, name + ".ini"),
-        help="name of the configuration file",
-    )
-    args = parser.parse_args()
+    global patch, name, path, monitor
 
-    config = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
-    config.read(args.inifile)
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    try:
-        r = redis.StrictRedis(
-            host=config.get("redis", "hostname"),
-            port=config.getint("redis", "port"),
-            db=0,
-            charset="utf-8",
-            decode_responses=True,
-        )
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug", default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -93,14 +69,11 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, device, rate, blocksize, modulation, nchans, frequencies, ntones, scale_amplitude, offset_amplitude, scale_frequency, offset_frequency,key, p, info, i, devinfo, stream, startfeedback, countfeedback, offset
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint("general", "debug"))
+    global patch, name, path, monitor
+    global debug, device, rate, blocksize, modulation, nchans, frequencies, ntones, scale_amplitude, offset_amplitude, scale_frequency, offset_frequency,key, p, info, i, devinfo, stream, startfeedback, countfeedback, offset
 
     # get the options from the configuration file
-    debug = patch.getint("general", "debug")
+    debug = patch.getint("general", "debug", default=1)
     device = patch.getint("audio", "device")
     rate = patch.getint("audio", "rate", default=44100)
     nchans = patch.getint("audio", "nchans", default=2)
@@ -129,7 +102,7 @@ def _start():
         for tone in range(0, ntones):
             tonestr = "tone%d" % (tone + 1)
             if patch.hasitem(channame[chan], tonestr):
-                redischannel = patch.getstring(channame[chan], tonestr)
+                redischannel = patch.get(channame[chan], tonestr)
                 key[chan].append(redischannel)
                 monitor.info("configured " + channame[chan] + " " + tonestr + " as " + redischannel)
             else:
@@ -175,7 +148,7 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global rate, nchans, blocksize, frequencies, modulation, ntones, offset, startfeedback, countfeedback
     global start, data, chan, timeaxis, tone, phase, value, Fsin, Fcos, chanval
 
diff --git a/module/endorphines/endorphines.py b/module/endorphines/endorphines.py
index a0cf5575..610cb5ff 100755
--- a/module/endorphines/endorphines.py
+++ b/module/endorphines/endorphines.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import threading
 import time
@@ -62,7 +59,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('ENDORPHINES_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)      # this message contains the note
         while self.running:
@@ -87,23 +84,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -114,14 +101,11 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, mididevice, outputport, lock, trigger, port, channel, previous_val, previous_port_val
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, mididevice, outputport, lock, trigger, port, channel, previous_val, previous_port_val
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     mididevice = patch.getstring('midi', 'device')
     mididevice = EEGsynth.trimquotes(mididevice)
     mididevice = process.extractOne(mididevice, mido.get_output_names())[0]  # select the closest match
@@ -147,10 +131,10 @@ def _start():
 
         # channels are one-offset in the ini file, zero-offset in the code
         name = 'channel{}'.format(channel + 1)
-        if config.has_option('gate', name):
+        if patch.config.has_option('gate', name):
 
             # start the background thread that deals with this channel
-            this = TriggerThread(patch.getstring('gate', name), channel)
+            this = TriggerThread(patch.get('gate', name), channel)
             trigger.append(this)
             monitor.debug(name + ' trigger configured')
 
@@ -175,8 +159,8 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, debug, mididevice, outputport, lock, trigger, port, channel, previous_val, previous_port_val
+    global patch, name, path, monitor
+    global debug, mididevice, outputport, lock, trigger, port, channel, previous_val, previous_port_val
     global name, val, port_val, scale, offset, midichannel, msg
 
     # loop over the control values
@@ -275,7 +259,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('ENDORPHINES_UNBLOCK', 1)
+    patch.publish('ENDORPHINES_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/example/example.py b/module/example/example.py
index 8edfb286..d8f5fb91 100755
--- a/module/example/example.py
+++ b/module/example/example.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 
@@ -52,23 +49,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -79,12 +66,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, prefix, param1, param2
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     prefix = patch.getstring('output', 'prefix')
     param1 = patch.getfloat('input', 'param1', default=0)
@@ -98,7 +82,7 @@ def _start():
 def _loop_once():
     '''Run the main loop once
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, prefix
 
     # update the value of input param1, copy it to the output
@@ -111,6 +95,7 @@ def _loop_once():
 
     monitor.info('param1', param1)  # this is printed when debug>=1
     monitor.debug('param2', param2) # this is printed when debug>=2
+    monitor.trace('param2', param2) # this is printed when debug>=3
 
 
 def _loop_forever():
diff --git a/module/generateclock/generateclock.py b/module/generateclock/generateclock.py
index 2d0e3d27..8e23d99f 100755
--- a/module/generateclock/generateclock.py
+++ b/module/generateclock/generateclock.py
@@ -17,14 +17,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import math
 import mido
 from fuzzywuzzy import process
 import numpy as np
 import os
-import redis
 import sys
 import threading
 import time
@@ -162,23 +159,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -189,12 +176,9 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, stepsize, scale_rate, offset_rate, scale_shift, offset_shift, scale_ppqn, offset_ppqn, lock, clock, i, clockthread, midithread, redisthread, midiport, previous_midi_play, previous_midi_start, previous_redis_play
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     stepsize = patch.getfloat('general', 'delay')
 
@@ -242,7 +226,7 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, stepsize, scale_rate, offset_rate, scale_shift, offset_shift, scale_ppqn, offset_ppqn, lock, clock, i, clockthread, midithread, redisthread, midiport, previous_midi_play, previous_midi_start, previous_redis_play
     global start, redis_play, midi_play, midi_start, rate, shift, ppqn, elapsed, naptime
 
diff --git a/module/generatecontrol/generatecontrol.py b/module/generatecontrol/generatecontrol.py
index 69876a19..cd4fdf26 100755
--- a/module/generatecontrol/generatecontrol.py
+++ b/module/generatecontrol/generatecontrol.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 from scipy import signal
@@ -53,23 +50,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -80,12 +67,9 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, stepsize, scale_frequency, scale_spread, scale_amplitude, scale_offset, scale_noise, scale_dutycycle, offset_frequency, offset_spread, offset_amplitude, offset_offset, offset_noise, offset_dutycycle, sample, phase
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     stepsize = patch.getfloat('generate', 'stepsize')  # in seconds
 
@@ -116,7 +100,7 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, stepsize, scale_frequency, scale_spread, scale_amplitude, scale_offset, scale_noise, scale_dutycycle, offset_frequency, offset_spread, offset_amplitude, offset_offset, offset_noise, offset_dutycycle, sample, phase
     global frequency, spread, amplitude, offset, noise, dutycycle, key, val, elapsed, naptime
 
diff --git a/module/generatesignal/generatesignal.py b/module/generatesignal/generatesignal.py
index 0af898f7..0d4ccd69 100755
--- a/module/generatesignal/generatesignal.py
+++ b/module/generatesignal/generatesignal.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 from scipy import signal as sp
@@ -55,29 +52,25 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_output
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # there should not be any local variables in this function, they should all be global
+    if len(locals()):
+        print("LOCALS: " + ", ".join(locals().keys()))
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
 
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, nchannels, fsample, shape, scale_frequency, scale_amplitude, scale_offset, scale_noise, scale_dutycycle, offset_frequency, offset_amplitude, offset_offset, offset_noise, offset_dutycycle, blocksize, datatype, block, begsample, endsample, stepsize, timevec, phasevec
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -89,18 +82,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to output FieldTrip buffer")
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print("LOCALS: " + ", ".join(locals().keys()))
-
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_output, name
-    global nchannels, fsample, shape, scale_frequency, scale_amplitude, scale_offset, scale_noise, scale_dutycycle, offset_frequency, offset_amplitude, offset_offset, offset_noise, offset_dutycycle, blocksize, datatype, block, begsample, endsample, stepsize, timevec, phasevec
-
     # get the options from the configuration file
     nchannels = patch.getint('generate', 'nchannels')
     fsample = patch.getfloat('generate', 'fsample')
@@ -161,8 +142,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_output
-    global nchannels, fsample, shape, scale_frequency, scale_amplitude, scale_offset, scale_noise, scale_dutycycle, offset_frequency, offset_amplitude, offset_offset, offset_noise, offset_dutycycle, blocksize, datatype, block, begsample, endsample, stepsize, timevec, phasevec
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, nchannels, fsample, shape, scale_frequency, scale_amplitude, scale_offset, scale_noise, scale_dutycycle, offset_frequency, offset_amplitude, offset_offset, offset_noise, offset_dutycycle, blocksize, datatype, block, begsample, endsample, stepsize, timevec, phasevec
     global start, frequency, amplitude, offset, noise, dutycycle, signal, dat_output, chan, elapsed, naptime
 
     if patch.getint('signal', 'rewind', default=0):
diff --git a/module/generatetrigger/generatetrigger.py b/module/generatetrigger/generatetrigger.py
index 42df4d7e..ca305ee2 100755
--- a/module/generatetrigger/generatetrigger.py
+++ b/module/generatetrigger/generatetrigger.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import threading
@@ -51,7 +48,7 @@
 
 
 def trigger(skip=False):
-    global monitor, debug, patch, lock, rate, spread, t
+    global debug, patch, lock, rate, spread, t
 
     if skip:
         # this happens upon initialization and when the timing parameters change
@@ -91,23 +88,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -118,14 +105,11 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, scale_rate, scale_spread, offset_rate, offset_spread, rate, spread, lock, t
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, scale_rate, scale_spread, offset_rate, offset_spread, rate, spread, lock, t
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # the scale and offset are used to map the Redis values to internal values
     scale_rate = patch.getfloat('scale', 'rate', default=1)
@@ -157,8 +141,8 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, debug, scale_rate, scale_spread, offset_rate, offset_spread, rate, spread, lock, t
+    global patch, name, path, monitor
+    global debug, scale_rate, scale_spread, offset_rate, offset_spread, rate, spread, lock, t
     global change
 
     with lock:
diff --git a/module/geomixer/geomixer.py b/module/geomixer/geomixer.py
index 2a8700e1..bcc24b12 100755
--- a/module/geomixer/geomixer.py
+++ b/module/geomixer/geomixer.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import math
 import numpy as np
 import os
-import redis
 import sys
 import threading
 import time
@@ -63,23 +60,13 @@ def _setup():
     """Initialize the module
     This adds a set of global variables
     """
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -90,14 +77,11 @@ def _start():
     """Start the module
     This uses the global variables from setup and adds a set of global variables
     """
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, stepsize, number, prefix, scale_input, scale_time, scale_precision, offset_input, offset_time, offset_precision, channel_name, vertex, dwelltime, edge, previous
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, stepsize, number, prefix, scale_input, scale_time, scale_precision, offset_input, offset_time, offset_precision, channel_name, vertex, dwelltime, edge, previous
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     stepsize = patch.getfloat('general', 'delay')
     number = patch.getint('switch', 'number', default=3)
     prefix = patch.getstring('output', 'prefix')
@@ -129,8 +113,8 @@ def _loop_once():
     """Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     """
-    global parser, args, config, r, response, patch
-    global monitor, debug, stepsize, number, prefix, scale_input, scale_time, scale_precision, offset_input, offset_time, offset_precision, channel_name, vertex, dwelltime, edge, previous
+    global patch, name, path, monitor
+    global debug, stepsize, number, prefix, scale_input, scale_time, scale_precision, offset_input, offset_time, offset_precision, channel_name, vertex, dwelltime, edge, previous
     global switch_time, switch_precision, input, lower_treshold, upper_treshold, change, key, channel_val, this, next, val, desired, elapsed, naptime, s
 
     # these can change on the fly
@@ -212,12 +196,11 @@ def _loop_once():
                 channel_val[next] = 1. - clip01(input)
         patch.setvalue(channel_name[this], channel_val[this])
 
-    if debug > 0:
-        # construct a string with all details on a single line
-        s = 'edge=%2d' % (edge)
-        for key, val in zip(channel_name, channel_val):
-            s += ' %s = %0.2f' % (key, val)
-        monitor.info(s)
+    # print all details on a single line for debugging
+    s = 'edge=%2d' % (edge)
+    for key, val in zip(channel_name, channel_val):
+        s += ' %s = %0.2f' % (key, val)
+    monitor.info(s)
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
diff --git a/module/heartrate/heartrate.py b/module/heartrate/heartrate.py
index 404e5c11..3c5d8a49 100755
--- a/module/heartrate/heartrate.py
+++ b/module/heartrate/heartrate.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -54,29 +51,28 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
+    # there should not be any local variables in this function, they should all be global
+    if len(locals()):
+        print('LOCALS: ' + ', '.join(locals().keys()))
 
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel, window, threshold, lrate, debounce, key_beat, key_rate, curvemin, curvemean, curvemax, prev, begsample, endsample
 
-    # get the options from the configuration file
-    debug = patch.getint('general','debug')
+    # this is the timeout for the FieldTrip buffer
+    timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -88,21 +84,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to input FieldTrip buffer")
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, channel, window, threshold, lrate, debounce, key_beat, key_rate, curvemin, curvemean, curvemax, prev, begsample, endsample
-
-    # this is the timeout for the FieldTrip buffer
-    timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
-
     hdr_input = None
     start = time.time()
     while hdr_input is None:
@@ -144,8 +125,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, channel, window, threshold, lrate, debounce, key_beat, key_rate, curvemin, curvemean, curvemax, prev, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel, window, threshold, lrate, debounce, key_beat, key_rate, curvemin, curvemean, curvemax, prev, begsample, endsample
     global dat, negrange, posrange, thresh, prevsample, sample, last, bpm, duration, duration_scale, duration_offset
 
     hdr_input = ft_input.getHeader()
@@ -230,7 +211,7 @@ def _loop_forever():
     while True:
         monitor.loop()
         _loop_once()
-        time.sleep(patch.getfloat('general','delay'))
+        time.sleep(patch.getfloat('general', 'delay'))
 
 
 def _stop():
diff --git a/module/historycontrol/historycontrol.py b/module/historycontrol/historycontrol.py
index d3009664..f2c2b4e2 100755
--- a/module/historycontrol/historycontrol.py
+++ b/module/historycontrol/historycontrol.py
@@ -18,11 +18,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from numpy import log, log2, log10, exp, power, sqrt, mean, median, var, std
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -61,23 +58,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -88,12 +75,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, inputlist, enable, stepsize, window, metrics_iqr, metrics_mad, metrics_max, metrics_max_att, metrics_mean, metrics_median, metrics_min, metrics_min_att, metrics_p03, metrics_p16, metrics_p84, metrics_p97, metrics_range, metrics_std, numchannel, numhistory, history, historic
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
-
     # get the options from the configuration file
     inputlist   = patch.getstring('input', 'channels', multiple=True)
     enable      = patch.getint('history', 'enable', default=1)
@@ -133,7 +117,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, inputlist, enable, stepsize, window, metrics_iqr, metrics_mad, metrics_max, metrics_max_att, metrics_mean, metrics_median, metrics_min, metrics_min_att, metrics_p03, metrics_p16, metrics_p84, metrics_p97, metrics_range, metrics_std, numchannel, numhistory, history, historic
     global prev_enable, channel, history_att, operation, key, val
 
@@ -159,7 +143,10 @@ def _loop_once():
 
     # update with current data
     for channel in range(numchannel):
-        history[channel, numhistory-1] = r.get(inputlist[channel])
+        try:
+            history[channel, numhistory-1] = float(patch.redis.get(inputlist[channel]))
+        except:
+            history[channel, numhistory-1] = np.NaN
 
     if metrics_mean or metrics_max_att or metrics_min_att:
         historic['mean']    = np.nanmean(history, axis=1)
diff --git a/module/historysignal/historysignal.py b/module/historysignal/historysignal.py
index 42e360e9..6a2a4aa8 100755
--- a/module/historysignal/historysignal.py
+++ b/module/historysignal/historysignal.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 from numpy import log, log2, log10, exp, power, sqrt, mean, median, var, std
@@ -64,29 +61,28 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
+    # there should not be any local variables in this function, they should all be global
+    if len(locals()):
+        print('LOCALS: ' + ', '.join(locals().keys()))
 
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, inputlist, prefix, enable, stepsize, window, numhistory, numchannel, history, historic, begsample, endsample
 
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    # this is the timeout for the FieldTrip buffer
+    timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -98,21 +94,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to input FieldTrip buffer")
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, inputlist, prefix, enable, stepsize, window, numhistory, numchannel, history, historic, begsample, endsample
-
-    # this is the timeout for the FieldTrip buffer
-    timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
-
     hdr_input = None
     start = time.time()
     while hdr_input is None:
@@ -159,8 +140,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, inputlist, prefix, enable, stepsize, window, numhistory, numchannel, history, historic, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, inputlist, prefix, enable, stepsize, window, numhistory, numchannel, history, historic, begsample, endsample
     global prev_enable, dat_input, chanindx, operation, key, val
 
     # determine the start of the actual processing
@@ -229,7 +210,7 @@ def _loop_once():
     for operation in list(historic.keys()):
         key = prefix + "." + operation
         val = historic[operation]
-        patch.setvalue(key, val, debug > 1)
+        patch.setvalue(key, val)
 
     begsample += stepsize
     endsample += stepsize
diff --git a/module/inputcontrol/inputcontrol.py b/module/inputcontrol/inputcontrol.py
index f9b5eb1d..68321f90 100755
--- a/module/inputcontrol/inputcontrol.py
+++ b/module/inputcontrol/inputcontrol.py
@@ -21,10 +21,7 @@
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtWidgets import QApplication, QWidget
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import signal
@@ -182,31 +179,31 @@ def drawmain(self):
         # the section 'slider' is treated as the first row
         # this is only for backward compatibility
         section = 'slider'
-        if config.has_section(section):
+        if patch.config.has_section(section):
             sectionlayout = QtWidgets.QHBoxLayout()
-            self.drawpanel(sectionlayout, config.items(section))
+            self.drawpanel(sectionlayout, patch.config.items(section))
             leftlayout.addLayout(sectionlayout)
 
         for row in range(0, 16):
             section = 'row%d' % (row + 1)
-            if config.has_section(section):
+            if patch.config.has_section(section):
                 sectionlayout = QtWidgets.QHBoxLayout()
-                self.drawpanel(sectionlayout, config.items(section))
+                self.drawpanel(sectionlayout, patch.config.items(section))
                 leftlayout.addLayout(sectionlayout)
 
         # the section 'button' is treated as the first column
         # this is only for backward compatibility
         section = 'button'
-        if config.has_section(section):
+        if patch.config.has_section(section):
             sectionlayout = QtWidgets.QVBoxLayout()
-            self.drawpanel(sectionlayout, config.items(section))
+            self.drawpanel(sectionlayout, patch.config.items(section))
             rightlayout.addLayout(sectionlayout)
 
         for column in range(0, 16):
             section = 'column%d' % (column + 1)
-            if config.has_section(section):
+            if patch.config.has_section(section):
                 sectionlayout = QtWidgets.QVBoxLayout()
-                self.drawpanel(sectionlayout, config.items(section))
+                self.drawpanel(sectionlayout, patch.config.items(section))
                 rightlayout.addLayout(sectionlayout)
 
     def changecolor(self):
@@ -310,23 +307,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -337,14 +324,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, prefix, winx, winy, winwidth, winheight, output_scale, output_offset
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, prefix, winx, winy, winwidth, winheight, output_scale, output_offset
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     prefix = patch.getstring('output', 'prefix')
     winx = patch.getfloat('display', 'xpos')
     winy = patch.getfloat('display', 'ypos')
@@ -355,9 +339,9 @@ def _start():
     output_scale = patch.getfloat('output', 'scale', default=1. / 127)  # internal values are from 0 to 127
     output_offset = patch.getfloat('output', 'offset', default=0.)    # internal values are from 0 to 127
 
-    if 'initial' in config.sections():
+    if 'initial' in patch.config.sections():
         # assign the initial values
-        for item in config.items('initial'):
+        for item in patch.config.items('initial'):
             val = patch.getfloat('initial', item[0])
             patch.setvalue(item[0], val)
             monitor.update(item[0], val)
diff --git a/module/inputcontrol/launchcontrol_mini.ini b/module/inputcontrol/launchcontrol_mini.ini
index 450ee6a7..8ef3b32f 100644
--- a/module/inputcontrol/launchcontrol_mini.ini
+++ b/module/inputcontrol/launchcontrol_mini.ini
@@ -47,6 +47,7 @@ note027=toggle1
 note028=toggle1
 
 [output]
+; the results will be written to Redis as "launchcontrol.control021" and "launchcontrol.control022" etc.
 prefix=launchcontrol
 
 ; the scale and offset are used to map the internal values between 0-127 to Redis
diff --git a/module/inputcontrol/launchcontrol_xl.ini b/module/inputcontrol/launchcontrol_xl.ini
index 9628ff3e..c42d03a7 100644
--- a/module/inputcontrol/launchcontrol_xl.ini
+++ b/module/inputcontrol/launchcontrol_xl.ini
@@ -84,7 +84,7 @@ note091=toggle1
 note092=toggle1
 
 [output]
-; the results will be written to Redis as "gui.slider065" and "gui.button012" etc.
+; the results will be written to Redis as "launchcontrol.control077" and "launchcontrol.control078" etc.
 prefix=launchcontrol
 
 ; the scale and offset are used to map the internal values between 0-127 to Redis
diff --git a/module/inputlsl/inputlsl.py b/module/inputlsl/inputlsl.py
index 476c6b72..d958d1a8 100755
--- a/module/inputlsl/inputlsl.py
+++ b/module/inputlsl/inputlsl.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import pylsl as lsl
@@ -53,23 +50,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -80,12 +67,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, delay, timeout, lsl_name, lsl_type, lsl_format, output_prefix, start, selected, streams, stream, inlet, type, source_id, match, lsl_id
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     delay = patch.getfloat('general', 'delay')
     timeout = patch.getfloat('lsl', 'timeout', default=30)
@@ -143,7 +127,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, delay, timeout, lsl_name, lsl_type, lsl_format, output_prefix, start, selected, streams, stream, inlet, type, source_id, match, lsl_id
     global sample, timestamp
 
diff --git a/module/inputmidi/inputmidi.py b/module/inputmidi/inputmidi.py
index a88fd930..f652f8d0 100755
--- a/module/inputmidi/inputmidi.py
+++ b/module/inputmidi/inputmidi.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import time
 
@@ -54,23 +51,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -81,14 +68,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, mididevice, output_scale, output_offset, port, inputport
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, mididevice, output_scale, output_offset, port, inputport
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     mididevice = patch.getstring('midi', 'device')
     mididevice = EEGsynth.trimquotes(mididevice)
 
@@ -117,8 +101,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, mididevice, output_scale, output_offset, port, inputport
+    global patch, name, path, monitor
+    global debug, mididevice, output_scale, output_offset, port, inputport
 
     for msg in inputport.iter_pending():
         monitor.debug(msg)
diff --git a/module/inputmqtt/inputmqtt.py b/module/inputmqtt/inputmqtt.py
index 1d4626c6..e6aa0a9b 100755
--- a/module/inputmqtt/inputmqtt.py
+++ b/module/inputmqtt/inputmqtt.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import string
 import sys
 import threading
@@ -85,30 +82,20 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, client
+    global patch, name, path, monitor, client
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     try:
         client = mqtt.Client()
-        client.connect(config.get('mqtt', 'hostname'), config.getint('mqtt', 'port'), config.getint('mqtt', 'timeout'))
+        client.connect(patch.get('mqtt', 'hostname'), patch.getint('mqtt', 'port'), patch.getint('mqtt', 'timeout'))
     except:
         raise RuntimeError("cannot connect to MQTT broker")
 
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
-
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
         print('LOCALS: ' + ', '.join(locals().keys()))
@@ -118,14 +105,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, client, name
-    global monitor, debug, prefix, output_scale, output_offset, input_channels, channel
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor, client, name
+    global debug, prefix, output_scale, output_offset, input_channels, channel
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     prefix = patch.getstring('output', 'prefix')
 
     # the scale and offset are used to map OSC values to Redis values
@@ -152,8 +136,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, client
-    global monitor, debug, prefix, output_scale, output_offset, input_channels, channel
+    global patch, name, path, monitor, client
+    global debug, prefix, output_scale, output_offset, input_channels, channel
     global output_scale, output_offset
 
     # update the scale and offset
diff --git a/module/inputosc/inputosc.py b/module/inputosc/inputosc.py
index aa2b203a..2a2b69d6 100755
--- a/module/inputosc/inputosc.py
+++ b/module/inputosc/inputosc.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import socket
 import sys
 import threading
@@ -111,23 +108,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -138,15 +125,12 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global use_old_version, dispatcher, osc_server
-    global monitor, debug, osc_address, osc_port, prefix, output_scale, output_offset
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global debug, osc_address, osc_port, prefix, output_scale, output_offset
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     osc_address = patch.getstring('osc', 'address', default=socket.gethostbyname(socket.gethostname()))
     osc_port = patch.getint('osc', 'port')
     prefix = patch.getstring('output', 'prefix')
@@ -189,7 +173,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global use_old_version, output_scale, output_offset
 
     # keep looping while incoming OSC messages are being handled
diff --git a/module/inputzeromq/inputzeromq.py b/module/inputzeromq/inputzeromq.py
index 481d3bb9..97b4a9e9 100755
--- a/module/inputzeromq/inputzeromq.py
+++ b/module/inputzeromq/inputzeromq.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import string
 import sys
 import time
@@ -54,34 +51,21 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, context, socket, patch
+    global patch, name, path, monitor, context, socket, patch
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    if not 'general' in config:
-        raise RuntimeError("cannot read configuration from " + args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     try:
         context = zmq.Context()
         socket = context.socket(zmq.SUB)
-        socket.connect("tcp://%s:%i" % (config.get('zeromq', 'hostname'), config.getint('zeromq', 'port')))
+        socket.connect("tcp://%s:%i" % (patch.get('zeromq', 'hostname'), patch.getint('zeromq', 'port')))
     except:
         raise RuntimeError("cannot connect to ZeroMQ")
 
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
-
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
         print('LOCALS: ' + ', '.join(locals().keys()))
@@ -91,14 +75,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, context, socket, patch
-    global monitor, debug, prefix, output_scale, output_offset, input_channels
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor, context, socket, patch
+    global debug, prefix, output_scale, output_offset, input_channels
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     prefix = patch.getstring('output', 'prefix')
 
     # the scale and offset are used to map OSC values to Redis values
@@ -126,8 +107,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, context, socket, patch
-    global monitor, debug, prefix, output_scale, output_offset, input_channels
+    global patch, name, path, monitor, context, socket, patch
+    global debug, prefix, output_scale, output_offset, input_channels
     global start
 
     start = time.time()
@@ -162,7 +143,7 @@ def _loop_once():
 def _loop_forever():
     '''Run the main loop forever
     '''
-    global monitor, patch
+    global monitor
     while True:
         monitor.loop()
         _loop_once()
diff --git a/module/keyboard/keyboard.py b/module/keyboard/keyboard.py
index ac94de2c..17b09e7d 100755
--- a/module/keyboard/keyboard.py
+++ b/module/keyboard/keyboard.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import threading
 import time
@@ -72,7 +69,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('KEYBOARD_UNBLOCK')  # this message unblocks the Redis listen command
         pubsub.subscribe(self.onset)      # this message triggers the note
         while self.running:
@@ -90,7 +87,7 @@ def run(self):
                         # use the value of the onset trigger
                         velocity = val
                     elif type(self.velocity) == str:
-                        velocity = float(r.get(self.velocity))
+                        velocity = float(patch.redis.get(self.velocity))
                         velocity = EEGsynth.rescale(velocity, slope=scale_velocity, offset=offset_velocity)
                         velocity = EEGsynth.limit(velocity, 0, 127)
                         velocity = int(velocity)
@@ -98,7 +95,7 @@ def run(self):
                         velocity = self.velocity
 
                     if type(self.pitch) == str:
-                        pitch = float(r.get(self.pitch))
+                        pitch = float(patch.redis.get(self.pitch))
                         pitch = EEGsynth.rescale(pitch, slope=scale_pitch, offset=offset_pitch)
                         pitch = EEGsynth.limit(pitch, 0, 127)
                         pitch = int(pitch)
@@ -106,7 +103,7 @@ def run(self):
                         pitch = self.pitch
 
                     if type(self.duration) == str:
-                        duration = float(r.get(self.duration))
+                        duration = float(patch.redis.get(self.duration))
                         duration = EEGsynth.rescale(duration, slope=scale_duration, offset=offset_duration)
                         # some minimal time is needed for the delay
                         duration = EEGsynth.limit(duration, 0.05, float('Inf'))
@@ -139,23 +136,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -166,11 +153,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, note_name, note_code, debug, midichannel, mididevice, input_scale, input_offset, scale_velocity, scale_pitch, scale_duration, offset_velocity, offset_pitch, offset_duration, output_scale, output_offset, port, inputport, outputport, lock, trigger, code, onset, velocity, pitch, duration, thread
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global note_name, note_code, debug, midichannel, mididevice, input_scale, input_offset, scale_velocity, scale_pitch, scale_duration, offset_velocity, offset_pitch, offset_duration, output_scale, output_offset, port, inputport, outputport, lock, trigger, code, onset, velocity, pitch, duration, thread
 
     # the list of MIDI commands is specific to the implementation for a full-scale keyboard
     # see https://newt.phys.unsw.edu.au/jw/notes.html
@@ -180,7 +164,7 @@ def _start():
                  82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143]
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     midichannel = patch.getint('midi', 'channel', default=None)
     mididevice = patch.getstring('midi', 'device')
     mididevice = EEGsynth.trimquotes(mididevice)
@@ -233,7 +217,7 @@ def _start():
     # the keyboard notes can be linked to separate triggers, where the trigger value corresponds to the velocity
     trigger = []
     for name, code in zip(note_name, note_code):
-        if config.has_option('input', name):
+        if patch.config.has_option('input', name):
             # start the background thread that deals with this note
             onset = patch.getstring('input', name)
             velocity = None  # use the value of the onset trigger
@@ -266,8 +250,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, note_name, note_code, debug, midichannel, mididevice, input_scale, input_offset, scale_velocity, scale_pitch, scale_duration, offset_velocity, offset_pitch, offset_duration, output_scale, output_offset, port, inputport, outputport, lock, trigger, code, onset, velocity, pitch, duration, thread
+    global patch, name, path, monitor
+    global note_name, note_code, debug, midichannel, mididevice, input_scale, input_offset, scale_velocity, scale_pitch, scale_duration, offset_velocity, offset_pitch, offset_duration, output_scale, output_offset, port, inputport, outputport, lock, trigger, code, onset, velocity, pitch, duration, thread
 
     for msg in inputport.iter_pending():
         if midichannel is None:
@@ -326,7 +310,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('KEYBOARD_UNBLOCK', 1)
+    patch.publish('KEYBOARD_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/launchcontrol/launchcontrol.py b/module/launchcontrol/launchcontrol.py
index b37e729c..78951936 100755
--- a/module/launchcontrol/launchcontrol.py
+++ b/module/launchcontrol/launchcontrol.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import time
 
@@ -59,26 +56,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    if not 'general' in config:
-        raise RuntimeError("cannot read configuration from " + args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -89,12 +73,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, push, toggle1, toggle2, toggle3, toggle4, slap, scale_note, scale_control, offset_note, offset_control, mididevice_input, mididevice_output, port, inputport, Off, Red_Low, Red_Full, Amber_Low, Amber_Full, Yellow_Full, Green_Low, Green_Full, ledcolor, note_list, status_list, note, state0change, state0color, state0value, state1change, state1color, state1value, state2change, state2color, state2value, state3change, state3color, state3value, state4change, state4color, state4value, state5change, state5color, state5value, midichannel, outputport
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     push        = patch.getint('button', 'push',    multiple=True)      # push-release button
     toggle1     = patch.getint('button', 'toggle1', multiple=True)      # on-off button
@@ -215,7 +196,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, push, toggle1, toggle2, toggle3, toggle4, slap, scale_note, scale_control, offset_note, offset_control, mididevice_input, mididevice_output, port, inputport, Off, Red_Low, Red_Full, Amber_Low, Amber_Full, Yellow_Full, Green_Low, Green_Full, ledcolor, note_list, status_list, note, state0change, state0color, state0value, state1change, state1color, state1value, state2change, state2color, state2value, state3change, state3color, state3value, state4change, state4color, state4value, state5change, state5color, state5value, midichannel, outputport
 
     for msg in inputport.iter_pending():
diff --git a/module/launchpad/launchpad.py b/module/launchpad/launchpad.py
index 81a26e11..35da7d20 100755
--- a/module/launchpad/launchpad.py
+++ b/module/launchpad/launchpad.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import time
 
@@ -59,23 +56,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -86,11 +73,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, push, toggle1, toggle2, toggle3, toggle4, slap, model, scale_note, scale_control, offset_note, offset_control, port, mididevice_input, mididevice_output, inputport, Off, Red_Full, Amber_Full, Yellow_Full, Green_Full, ledcolor, note_list, status_list, note, state0change, state0color, state0value, state1change, state1color, state1value, state2change, state2color, state2value, state3change, state3color, state3value, state4change, state4color, state4value, state5change, state5color, state5value, midichannel, outputport
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+    global patch, name, path, monitor
+    global debug, push, toggle1, toggle2, toggle3, toggle4, slap, model, scale_note, scale_control, offset_note, offset_control, port, mididevice_input, mididevice_output, inputport, Off, Red_Full, Amber_Full, Yellow_Full, Green_Full, ledcolor, note_list, status_list, note, state0change, state0color, state0value, state1change, state1color, state1value, state2change, state2color, state2value, state3change, state3color, state3value, state4change, state4color, state4value, state5change, state5color, state5value, midichannel, outputport
 
     # get the options from the configuration file
     debug       = patch.getint('general','debug')
@@ -226,8 +210,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, push, toggle1, toggle2, toggle3, toggle4, slap, model, scale_note, scale_control, offset_note, offset_control, port, mididevice_input, mididevice_output, inputport, Off, Red_Full, Amber_Full, Yellow_Full, Green_Full, ledcolor, note_list, status_list, note, state0change, state0color, state0value, state1change, state1color, state1value, state2change, state2color, state2value, state3change, state3color, state3value, state4change, state4color, state4value, state5change, state5color, state5value, midichannel, outputport
+    global patch, name, path, monitor
+    global debug, push, toggle1, toggle2, toggle3, toggle4, slap, model, scale_note, scale_control, offset_note, offset_control, port, mididevice_input, mididevice_output, inputport, Off, Red_Full, Amber_Full, Yellow_Full, Green_Full, ledcolor, note_list, status_list, note, state0change, state0color, state0value, state1change, state1color, state1value, state2change, state2color, state2value, state3change, state3color, state3value, state4change, state4color, state4value, state5change, state5color, state5value, midichannel, outputport
 
     for msg in inputport.iter_pending():
         if midichannel is None:
@@ -326,7 +310,7 @@ def _loop_forever():
     while True:
         monitor.loop()
         _loop_once()
-        time.sleep(patch.getfloat('general','delay'))
+        time.sleep(patch.getfloat('general', 'delay'))
 
 
 def _stop():
diff --git a/module/lsl2ft/lsl2ft.py b/module/lsl2ft/lsl2ft.py
index 1fa02f5a..f7aeb93d 100755
--- a/module/lsl2ft/lsl2ft.py
+++ b/module/lsl2ft/lsl2ft.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import pylsl as lsl
@@ -55,23 +52,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -82,12 +69,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, timeout, lsl_name, lsl_type, ft_host, ft_port, ft_output, start, selected, streams, stream, inlet, type, source_id, match, lsl_id, channel_count, channel_format, nominal_srate, samples, blocksize
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     timeout = patch.getfloat('lsl', 'timeout', default=30)
     lsl_name = patch.getstring('lsl', 'name')
@@ -162,7 +146,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, timeout, lsl_name, lsl_type, ft_host, ft_port, ft_output, start, selected, streams, stream, inlet, type, source_id, match, lsl_id, channel_count, channel_format, nominal_srate, samples, blocksize
 
     chunk, timestamps = inlet.pull_chunk()
diff --git a/module/modulatetone/modulatetone.py b/module/modulatetone/modulatetone.py
index 252dd608..e98e9171 100755
--- a/module/modulatetone/modulatetone.py
+++ b/module/modulatetone/modulatetone.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import signal
@@ -65,7 +62,7 @@ def stop(self):
 
     def run(self):
         global modulation, frequencies, control, channame, scale_amplitude, offset_amplitude, scale_frequency, offset_frequency
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         # this message unblocks the Redis listen command
         pubsub.subscribe('MODULATETONE_UNBLOCK')
         # this message triggers the event
@@ -128,31 +125,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'),
-                        help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint(
-            'redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -163,7 +142,7 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug
+    global patch, name, path, monitor
     global device, mode, rate, modulation, offset, control, scale_amplitude, offset_amplitude, scale_frequency, offset_frequency, nchans, channame, ntones, frequencies, lock, stream, trigger, thread, p
 
     # get the options from the configuration file
@@ -203,7 +182,7 @@ def _start():
         for tone in range(0, ntones):
             tonestr = "tone%d" % (tone + 1)
             if patch.hasitem(channame[chan], tonestr):
-                redischannel = patch.getstring(channame[chan], tonestr)
+                redischannel = patch.get(channame[chan], tonestr)
                 trigger.append(TriggerThread(redischannel, chan, tone))
                 monitor.info("configured " + channame[chan] + " " + tonestr + " as " + redischannel)
             else:
@@ -276,7 +255,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('MODULATETONE_UNBLOCK', 1)
+    patch.publish('MODULATETONE_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     stream.stop_stream()
diff --git a/module/openbci2ft/openbci2ft.py b/module/openbci2ft/openbci2ft.py
new file mode 100755
index 00000000..e69de29b
diff --git a/module/outputartnet/outputartnet.py b/module/outputartnet/outputartnet.py
index 583fffad..25ea3a1f 100755
--- a/module/outputartnet/outputartnet.py
+++ b/module/outputartnet/outputartnet.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 
@@ -53,23 +50,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -80,11 +67,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, address, artnet, dmxsize, dmxframe, prevtime
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+    global patch, name, path, monitor
+    global debug, address, artnet, dmxsize, dmxframe, prevtime
 
     # get the options from the configuration file
     debug = patch.getint('general','debug')
@@ -95,7 +79,7 @@ def _start():
 
     # determine the size of the universe
     dmxsize = 0
-    chanlist, chanvals = list(map(list, list(zip(*config.items('input')))))
+    chanlist, chanvals = list(map(list, list(zip(*patch.config.items('input')))))
     for chanindx in range(0, 512):
         chanstr = "channel%03d" % (chanindx + 1)
         if chanstr in chanlist:
@@ -123,8 +107,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, address, artnet, dmxsize, dmxframe, prevtime
+    global patch, name, path, monitor
+    global debug, address, artnet, dmxsize, dmxframe, prevtime
     global update, chanindx, chanstr, chanval, scale, offset
 
     update = False
diff --git a/module/outputaudio/outputaudio.py b/module/outputaudio/outputaudio.py
index afb08e02..5d57990c 100755
--- a/module/outputaudio/outputaudio.py
+++ b/module/outputaudio/outputaudio.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import signal
@@ -113,29 +110,28 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
+    # there should not be any local variables in this function, they should all be global
+    if len(locals()):
+        print('LOCALS: ' + ', '.join(locals().keys()))
 
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, device, window, lrate, scaling_method, scaling, outputrate, scale_scaling, offset_scaling, nchans, inputrate, p, info, i, devinfo, lock, stack, firstsample, stretch, inputblock, outputblock, previnput, prevoutput, stream, begsample, endsample
 
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    # this is the timeout for the FieldTrip buffer
+    timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -147,21 +143,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to input FieldTrip buffer")
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, device, window, lrate, scaling_method, scaling, outputrate, scale_scaling, offset_scaling, nchans, inputrate, p, info, i, devinfo, lock, stack, firstsample, stretch, inputblock, outputblock, previnput, prevoutput, stream, begsample, endsample
-
-    # this is the timeout for the FieldTrip buffer
-    timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
-
     hdr_input = None
     start = time.time()
     while hdr_input is None:
@@ -253,8 +234,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, device, window, lrate, scaling_method, scaling, outputrate, scale_scaling, offset_scaling, nchans, inputrate, p, info, i, devinfo, lock, stack, firstsample, stretch, inputblock, outputblock, previnput, prevoutput, stream, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, device, window, lrate, scaling_method, scaling, outputrate, scale_scaling, offset_scaling, nchans, inputrate, p, info, i, devinfo, lock, stack, firstsample, stretch, inputblock, outputblock, previnput, prevoutput, stream, begsample, endsample
     global dat, now, old, new, duration
 
     # measure the time that it takes
@@ -333,10 +314,12 @@ def _loop_forever():
         _loop_once()
 
 
-def _stop():
+def _stop(*args):
     '''Stop and clean up on SystemExit, KeyboardInterrupt
     '''
-    global stream, p
+    global monitor, ft_input, stream, p
+    ft_input.disconnect()
+    monitor.success('Disconnected from input FieldTrip buffer')
     stream.stop_stream()
     stream.close()
     p.terminate()
diff --git a/module/outputcvgate/outputcvgate.py b/module/outputcvgate/outputcvgate.py
index 252f3290..c18aa5b0 100755
--- a/module/outputcvgate/outputcvgate.py
+++ b/module/outputcvgate/outputcvgate.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import threading
 import time
@@ -76,7 +73,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         # this message unblocks the Redis listen command
         pubsub.subscribe('OUTPUTCVGATE_UNBLOCK')
         # this message triggers the event
@@ -123,22 +120,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -149,12 +137,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, duration_scale, duration_offset, serialdevice, s, lock, trigger, chanindx, chanstr, redischannel, thread
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # values between 0 and 1 work well for the duration
     duration_scale = patch.getfloat('duration', 'scale', default=1)
     duration_offset = patch.getfloat('duration', 'offset', default=0)
@@ -202,7 +187,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, duration_scale, duration_offset, serialdevice, s, lock, trigger, chanindx, chanstr, redischannel, thread
 
     # loop over the control voltages
@@ -267,7 +252,7 @@ def _stop():
     monitor.success("Closing threads")
     for thread in trigger:
         thread.stop()
-    r.publish('OUTPUTCVGATE_UNBLOCK', 1)
+    patch.publish('OUTPUTCVGATE_UNBLOCK', 1)
     for thread in trigger:
             thread.join()
     sys.exit()
diff --git a/module/outputdmx/outputdmx.py b/module/outputdmx/outputdmx.py
index a1de0218..e429e6f5 100755
--- a/module/outputdmx/outputdmx.py
+++ b/module/outputdmx/outputdmx.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import serial
@@ -64,23 +61,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -91,14 +78,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, serialdevice, s, dmxsize, chanlist, chanvals, chanindx, chanstr, dmxframe, prevtime, START_VAL, END_VAL, TX_DMX_PACKET, FRAME_PAD
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, serialdevice, s, dmxsize, chanlist, chanvals, chanindx, chanstr, dmxframe, prevtime, START_VAL, END_VAL, TX_DMX_PACKET, FRAME_PAD
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # get the specified serial device, or the one that is the closest match
     serialdevice = patch.getstring('serial', 'device')
@@ -113,7 +97,7 @@ def _start():
 
     # determine the size of the universe
     dmxsize = 0
-    chanlist, chanvals = list(map(list, list(zip(*config.items('input')))))
+    chanlist, chanvals = list(map(list, list(zip(*patch.config.items('input')))))
     for chanindx in range(0, 512):
         chanstr = "channel%03d" % (chanindx + 1)
         if chanstr in chanlist:
@@ -141,8 +125,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, serialdevice, s, dmxsize, chanlist, chanvals, chanindx, chanstr, dmxframe, prevtime
+    global patch, name, path, monitor
+    global debug, serialdevice, s, dmxsize, chanlist, chanvals, chanindx, chanstr, dmxframe, prevtime
     global update, chanval, scale, offset
 
     update = False
diff --git a/module/outputgpio/outputgpio.py b/module/outputgpio/outputgpio.py
index 195e9523..7ecf6393 100755
--- a/module/outputgpio/outputgpio.py
+++ b/module/outputgpio/outputgpio.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import threading
@@ -73,7 +70,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         # this message unblocks the Redis listen command
         pubsub.subscribe('OUTPUTGPIO_UNBLOCK')
         # this message triggers the event
@@ -105,23 +102,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -132,11 +119,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, pin, debug, delay, scale_duration, offset_duration, lock, trigger
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global pin, debug, delay, scale_duration, offset_duration, lock, trigger
 
     # make a dictionary that maps GPIOs to the WiringPi number
     pin = {
@@ -160,7 +144,7 @@ def _start():
     }
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     delay = patch.getfloat('general', 'delay')
 
     # values between 0 and 1 work well for the duration
@@ -175,7 +159,7 @@ def _start():
 
     # set up PWM for the control channels
     previous_val = {}
-    for gpio, channel in config.items('control'):
+    for gpio, channel in patch.config.items('control'):
         monitor.info("control " + channel + " " + gpio)
         wiringpi.softPwmCreate(pin[gpio], 0, 100)
         # control values are only relevant when different from the previous value
@@ -183,7 +167,7 @@ def _start():
 
     # create the threads that deal with the triggers
     trigger = []
-    for gpio, channel in config.items('trigger'):
+    for gpio, channel in patch.config.items('trigger'):
         wiringpi.pinMode(pin[gpio], 1)
         duration = patch.getstring('duration', gpio)
         trigger.append(TriggerThread(channel, gpio, duration))
@@ -202,9 +186,9 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    for gpio, channel in config.items('control'):
+    for gpio, channel in patch.config.items('control'):
         val = patch.getfloat('control', gpio)
 
         if val == None:
@@ -243,7 +227,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('OUTPUTGPIO_UNBLOCK', 1)
+    patch.publish('OUTPUTGPIO_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/outputlsl/outputlsl.py b/module/outputlsl/outputlsl.py
index 29e1280b..87669d79 100755
--- a/module/outputlsl/outputlsl.py
+++ b/module/outputlsl/outputlsl.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
 import random
-import redis
 import string
 import sys
 import threading
@@ -52,12 +49,6 @@
 import EEGsynth
 
 
-def randomStringDigits(stringLength=6):
-    """Generate a random string of digits """
-    letters = string.digits
-    return ''.join(random.choice(letters) for i in range(stringLength))
-
-
 class TriggerThread(threading.Thread):
     def __init__(self, redischannel, trigger):
         threading.Thread.__init__(self)
@@ -70,7 +61,7 @@ def stop(self):
 
     def run(self):
         global r, lsl_format, patch, lock, monitor, outlet
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('OUTPUTLSL_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)  # this message contains the trigger
         while self.running:
@@ -98,23 +89,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -125,18 +106,14 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, lsl_name, lsl_type, lsl_id, lsl_format, info, outlet, trigger, item, lock, thread, previous_val
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, lsl_name, lsl_type, lsl_id, lsl_format, info, outlet, trigger, item, lock, thread, previous_val
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
-
+    debug = patch.getint('general', 'debug', default=1)
     lsl_name = patch.getstring('lsl', 'name', default='eegsynth')
     lsl_type = patch.getstring('lsl', 'type', default='Markers')
-    lsl_id = patch.getstring('lsl', 'id', default=randomStringDigits())
+    lsl_id = patch.getstring('lsl', 'id', default=EEGsynth.uuid(6))
     lsl_format = patch.getstring('lsl', 'format')
 
     # create an outlet stream
@@ -146,7 +123,7 @@ def _start():
     # create the background threads that deal with the triggers
     trigger = []
     monitor.info("Setting up threads for each trigger")
-    for item in config.items('trigger'):
+    for item in patch.config.items('trigger'):
         trigger.append(TriggerThread(item[1], item[0]))
         monitor.debug(str(item[0]) + " " + str(item[1]) + " OK")
 
@@ -158,7 +135,7 @@ def _start():
         thread.start()
 
     previous_val = {}
-    for item in config.items('control'):
+    for item in patch.config.items('control'):
         key = item[0]
         previous_val[key] = None
 
@@ -172,12 +149,12 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, lsl_name, lsl_type, lsl_id, lsl_format, info, outlet, trigger, item, lock, thread, previous_val
+    global patch, name, path, monitor
+    global debug, lsl_name, lsl_type, lsl_id, lsl_format, info, outlet, trigger, item, lock, thread, previous_val
     global val, marker
 
     # loop over the control values
-    for item in config.items('control'):
+    for item in patch.config.items('control'):
         key = item[0]
 
         val = patch.getfloat('control', key)
@@ -223,7 +200,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('OUTPUTLSL_UNBLOCK', 1)
+    patch.publish('OUTPUTLSL_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/outputmidi/outputmidi.py b/module/outputmidi/outputmidi.py
index 9fdfa2f0..2822b392 100755
--- a/module/outputmidi/outputmidi.py
+++ b/module/outputmidi/outputmidi.py
@@ -19,15 +19,13 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import threading
 import time
+import numpy as np
 
 if hasattr(sys, 'frozen'):
     path = os.path.split(sys.executable)[0]
@@ -52,7 +50,7 @@
 
 
 def UpdateParameters():
-    global patch, velocity_note, scale_velocity, offset_velocity, duration_note, scale_duration, offset_duration
+    global patch, name, path, monitor, velocity_note, scale_velocity, offset_velocity, duration_note, scale_duration, offset_duration
     velocity_note = patch.getfloat('velocity', 'note', default=64)
     velocity_note = int(EEGsynth.rescale(velocity_note, slope=scale_velocity, offset=offset_velocity))
     duration_note = patch.getfloat('duration', 'note', default=None)
@@ -98,6 +96,10 @@ def SetNoteOff(note, velocity):
 # send the MIDI message, different messages have slightly different parameters
 def sendMidi(name, code, val):
     global previous
+    
+    if np.isnan(val):
+        # monitor.error('cannot send NaN as MIDI message')
+        return
 
     if name == 'pitchwheel':
         # the value should be limited between -8192 to 8191
@@ -162,7 +164,7 @@ def __init__(self, redischannel, name, code):
     def stop(self):
         self.running = False
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('OUTPUTMIDI_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)     # this message contains the value of interest
         while self.running:
@@ -185,23 +187,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -212,14 +204,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global debug, mididevice, port, previous_note, trigger_name, trigger_code, code, trigger, this, thread, control_name, control_code, previous_val, duration_note, lock, midichannel, monitor, monophonic, offset_duration, offset_velocity, outputport, scale_duration, scale_velocity, velocity_note
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
-
     # get the options from the configuration file
-    debug       = patch.getint('general', 'debug')
+    debug       = patch.getint('general', 'debug', default=1)
     monophonic  = patch.getint('general', 'monophonic', default=1)
     midichannel = patch.getint('midi', 'channel')-1  # channel 1-16 get mapped to 0-15
     mididevice  = patch.getstring('midi', 'device')
@@ -274,9 +263,9 @@ def _start():
     # each of the Redis messages is mapped onto a different MIDI message
     trigger = []
     for name, code in zip(trigger_name, trigger_code):
-        if config.has_option('trigger', name):
+        if patch.config.has_option('trigger', name):
             # start the background thread that deals with this note
-            this = TriggerThread(patch.getstring('trigger', name), name, code)
+            this = TriggerThread(patch.get('trigger', name), name, code)
             trigger.append(this)
             monitor.debug(name + ' trigger configured')
 
@@ -311,7 +300,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global debug, mididevice, port, previous_note, trigger_name, trigger_code, code, trigger, this, thread, control_name, control_code, previous_val, duration_note, lock, midichannel, monitor, monophonic, offset_duration, offset_velocity, outputport, scale_duration, scale_velocity, velocity_note
 
     UpdateParameters()
@@ -351,7 +340,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('OUTPUTMIDI_UNBLOCK', 1)
+    patch.publish('OUTPUTMIDI_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
 
diff --git a/module/outputmqtt/outputmqtt.py b/module/outputmqtt/outputmqtt.py
index 22555e90..b1b46b84 100755
--- a/module/outputmqtt/outputmqtt.py
+++ b/module/outputmqtt/outputmqtt.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import string
 import sys
 import threading
@@ -63,7 +60,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('OUTPUTMQTT_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)     # this message contains the value of interest
         while self.running:
@@ -108,23 +105,10 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -135,18 +119,18 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, list_input, list_output, list1, list2, list3, i, j, lock, trigger, key1, key2, key3, this, thread, client
+    global patch, name, path, monitor
+    global debug, list_input, list_output, list1, list2, list3, i, j, lock, trigger, key1, key2, key3, this, thread, client
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # keys should be present in both the input and output section of the *.ini file
-    list_input = config.items('input')
-    list_output = config.items('output')
+    list_input = patch.config.items('input')
+    list_output = patch.config.items('output')
 
     list1 = []  # the key name that matches in the input and output section of the *.ini file
     list2 = []  # the key name in Redis
@@ -175,7 +159,7 @@ def _start():
     # make the connection with the MQTT broker
     try:
         client = mqtt.Client()
-        client.connect(config.get('mqtt', 'hostname'), config.getint('mqtt', 'port'), config.getint('mqtt', 'timeout'))
+        client.connect(patch.get('mqtt', 'hostname'), patch.getint('mqtt', 'port'), patch.getint('mqtt', 'timeout'))
         client.on_connect = on_connect
         client.on_message = on_message
         client.on_disconnect = on_disconnect
@@ -210,7 +194,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('OUTPUTMQTT_UNBLOCK', 1)
+    patch.publish('OUTPUTMQTT_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/outputosc/outputosc.py b/module/outputosc/outputosc.py
index 77501c02..54b68414 100755
--- a/module/outputosc/outputosc.py
+++ b/module/outputosc/outputosc.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import threading
 import time
@@ -76,7 +73,7 @@ def __init__(self, redischannel, name, osctopic):
     def stop(self):
         self.running = False
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('OUTPUTOSC_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)     # this message contains the value of interest
         while self.running:
@@ -107,23 +104,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -134,14 +121,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, s, list_input, list_output, list1, list2, list3, i, j, lock, trigger, key1, key2, key3, this, thread
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+    global patch, name, path, monitor
+    global debug, s, list_input, list_output, list1, list2, list3, i, j, lock, trigger, key1, key2, key3, this, thread
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     try:
         if use_old_version:
@@ -154,8 +138,8 @@ def _start():
         raise RuntimeError("Cannot connect to OSC server")
 
     # keys should be present in both the input and output section of the *.ini file
-    list_input  = config.items('input')
-    list_output = config.items('output')
+    list_input  = patch.config.items('input')
+    list_output = patch.config.items('output')
 
     list1 = [] # the key name that matches in the input and output section of the *.ini file
     list2 = [] # the key name in Redis
@@ -209,7 +193,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('OUTPUTOSC_UNBLOCK', 1)
+    patch.publish('OUTPUTOSC_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/outputzeromq/__init__.py b/module/outputzeromq/__init__.py
old mode 100644
new mode 100755
diff --git a/module/outputzeromq/outputzeromq.py b/module/outputzeromq/outputzeromq.py
index bf5811bf..8ea679b5 100755
--- a/module/outputzeromq/outputzeromq.py
+++ b/module/outputzeromq/outputzeromq.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import string
 import sys
 import threading
@@ -64,7 +61,7 @@ def stop(self):
 
     def run(self):
         global r, patch, monitor, socket
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('OUTPUTZEROMQ_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)       # this message contains the value of interest
         while self.running:
@@ -91,26 +88,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    if not 'general' in config:
-        raise RuntimeError("cannot read configuration from " + args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -121,18 +105,15 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, list_input, list_output, list1, list2, list3, i, j, lock, trigger, key1, key2, key3, this, thread, context, socket
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, list_input, list_output, list1, list2, list3, i, j, lock, trigger, key1, key2, key3, this, thread, context, socket
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # keys should be present in both the input and output section of the *.ini file
-    list_input = config.items('input')
-    list_output = config.items('output')
+    list_input = patch.config.items('input')
+    list_output = patch.config.items('output')
 
     list1 = []  # the key name that matches in the input and output section of the *.ini file
     list2 = []  # the key name in Redis
@@ -195,7 +176,7 @@ def _stop():
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('OUTPUTZEROMQ_UNBLOCK', 1)
+    patch.publish('OUTPUTZEROMQ_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     context.destroy()
diff --git a/module/playbackcontrol/playbackcontrol.py b/module/playbackcontrol/playbackcontrol.py
index a2814068..fab8519c 100755
--- a/module/playbackcontrol/playbackcontrol.py
+++ b/module/playbackcontrol/playbackcontrol.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -54,23 +51,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -81,14 +68,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, filename, f, chanindx, channels, channelz, fSample, nSamples, replace, i, s, z, blocksize, begsample, endsample, block
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, filename, f, chanindx, channels, channelz, fSample, nSamples, replace, i, s, z, blocksize, begsample, endsample, block
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     filename = patch.getstring('playback', 'file')
 
     monitor.info("Reading data from " + filename)
@@ -114,7 +98,7 @@ def _start():
     nSamples = f.getNSamples()[0]
 
     # search-and-replace to reduce the length of the channel labels
-    for replace in config.items('replace'):
+    for replace in patch.config.items('replace'):
         monitor.debug(replace)
         for i in range(len(channelz)):
             channelz[i] = channelz[i].replace(replace[0], replace[1])
@@ -136,8 +120,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, filename, f, chanindx, channels, channelz, fSample, nSamples, replace, i, s, z, blocksize, begsample, endsample, block
+    global patch, name, path, monitor
+    global debug, filename, f, chanindx, channels, channelz, fSample, nSamples, replace, i, s, z, blocksize, begsample, endsample, block
     global indx, val, stepsize, elapsed, naptime
 
     if endsample > nSamples - 1:
diff --git a/module/playbacksignal/playbacksignal.py b/module/playbacksignal/playbacksignal.py
index ce19906b..5867c70d 100755
--- a/module/playbacksignal/playbacksignal.py
+++ b/module/playbacksignal/playbacksignal.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import wave
@@ -57,23 +54,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-i', '--inifile', default=os.path.join(path, name + '.ini'), help='name of the configuration file')
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError('cannot connect to Redis server')
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -84,11 +71,18 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, filename, fileformat, ext, ft_host, ft_port, ft_output, H, MININT8, MAXINT8, MININT16, MAXINT16, MININT32, MAXINT32, f, chanindx, labels, A, blocksize, begsample, endsample, block
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, filename, fileformat, ext, H, MININT8, MAXINT8, MININT16, MAXINT16, MININT32, MAXINT32, f, chanindx, labels, A, blocksize, begsample, endsample, block
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    try:
+        ft_host = patch.getstring('fieldtrip', 'hostname')
+        ft_port = patch.getint('fieldtrip', 'port')
+        monitor.success('Trying to connect to buffer on %s:%i ...' % (ft_host, ft_port))
+        ft_output = FieldTrip.Client()
+        ft_output.connect(ft_host, ft_port)
+        monitor.success('Connected to FieldTrip buffer')
+    except:
+        raise RuntimeError('cannot connect to FieldTrip buffer')
 
     # get the options from the configuration file
     filename = patch.getstring('playback', 'file')
@@ -101,16 +95,6 @@ def _start():
 
     monitor.info('Reading data from ' + filename)
 
-    try:
-        ft_host = patch.getstring('fieldtrip', 'hostname')
-        ft_port = patch.getint('fieldtrip', 'port')
-        monitor.success('Trying to connect to buffer on %s:%i ...' % (ft_host, ft_port))
-        ft_output = FieldTrip.Client()
-        ft_output.connect(ft_host, ft_port)
-        monitor.success('Connected to FieldTrip buffer')
-    except:
-        raise RuntimeError('cannot connect to FieldTrip buffer')
-
     H = FieldTrip.Header()
 
     MININT8 = -np.power(2., 7)
@@ -202,8 +186,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, H, A, ft_output, blocksize, begsample, endsample, block
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_output, filename, fileformat, ext, H, MININT8, MAXINT8, MININT16, MAXINT16, MININT32, MAXINT32, f, chanindx, labels, A, blocksize, begsample, endsample, block
     global D, stepsize
 
     if endsample > H.nSamples - 1:
diff --git a/module/plotcontrol/plotcontrol.py b/module/plotcontrol/plotcontrol.py
index a1a4e6c5..e80aa1c7 100755
--- a/module/plotcontrol/plotcontrol.py
+++ b/module/plotcontrol/plotcontrol.py
@@ -20,9 +20,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from pyqtgraph.Qt import QtGui, QtCore
-import configparser
-import redis
-import argparse
 import numpy as np
 import os
 import pyqtgraph as pg
@@ -54,35 +51,22 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
 
 def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, delay, historysize, window, winx, winy, winwidth, winheight, input_name, input_variable, ylim_name, ylim_value, counter, app, win, inputhistory, inputplot, inputcurve, iplot, name, ylim, variable, linecolor, icurve, timer, timeaxis
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     delay       = patch.getfloat('general', 'delay')
     window      = patch.getfloat('general', 'window') # in seconds
@@ -94,8 +78,8 @@ def _start():
     historysize = int(window/delay) # in steps
     timeaxis = np.linspace(-window, 0, historysize)
 
-    input_name, input_variable = list(zip(*config.items('input')))
-    ylim_name, ylim_value = list(zip(*config.items('ylim')))
+    input_name, input_variable = list(zip(*patch.config.items('input')))
+    ylim_name, ylim_value = list(zip(*patch.config.items('ylim')))
 
     # count the total number of curves to be drawm
     counter = 0
@@ -138,20 +122,20 @@ def _start():
 
         win.nextRow()
 
-        signal.signal(signal.SIGINT, _stop)
+    signal.signal(signal.SIGINT, _stop)
 
-        # Set timer for update
-        timer = QtCore.QTimer()
-        timer.timeout.connect(_loop_once)
-        timer.setInterval(10)            # timeout in milliseconds
-        timer.start(int(delay * 1000))   # in milliseconds
+    # Set timer for update
+    timer = QtCore.QTimer()
+    timer.timeout.connect(_loop_once)
+    timer.setInterval(10)            # timeout in milliseconds
+    timer.start(int(delay * 1000))   # in milliseconds
 
 
 def _loop_once():
     '''Update the main figure once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, delay, historysize, window, winx, winy, winwidth, winheight, input_name, input_variable, ylim_name, ylim_value, counter, app, win, inputhistory, inputplot, inputcurve, iplot, name, ylim, variable, linecolor, icurve, timer, timeaxis
 
     monitor.loop()
diff --git a/module/plotimage/plotimage.py b/module/plotimage/plotimage.py
index b56ffc11..0b224c7c 100755
--- a/module/plotimage/plotimage.py
+++ b/module/plotimage/plotimage.py
@@ -21,9 +21,6 @@
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtWidgets import QApplication, QWidget
-import configparser
-import redis
-import argparse
 import os
 import sys
 import time
@@ -66,7 +63,7 @@ def stop(self):
 
     def run(self):
         global r, patch, lock, monitor
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('PLOTIMAGE_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)  # this message contains the trigger
         while self.running:
@@ -108,23 +105,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -135,12 +122,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, delay, winx, winy, winwidth, winheight, input_channel, input_image, app, window, triggers, timer
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     delay           = patch.getfloat('general', 'delay')
     winx            = patch.getfloat('display', 'xpos')
@@ -149,7 +133,7 @@ def _start():
     winheight       = patch.getfloat('display', 'height')
 
     # get the input options
-    input_channel, input_image = list(zip(*config.items('input')))
+    input_channel, input_image = list(zip(*patch.config.items('input')))
 
     # start the graphical user interface
     app = QApplication(sys.argv)
@@ -198,7 +182,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in triggers:
         thread.stop()
-    r.publish('PLOTIMAGE_UNBLOCK', 1)
+    patch.publish('PLOTIMAGE_UNBLOCK', 1)
     for thread in triggers:
         thread.join()
     QApplication.quit()
diff --git a/module/plotsignal/plotsignal.py b/module/plotsignal/plotsignal.py
index 3c3c38fc..6791e172 100755
--- a/module/plotsignal/plotsignal.py
+++ b/module/plotsignal/plotsignal.py
@@ -20,9 +20,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from pyqtgraph.Qt import QtGui, QtCore
-import configparser
-import redis
-import argparse
 import numpy as np
 import os
 import pyqtgraph as pg
@@ -58,29 +55,21 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
-
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, channels, winx, winy, winwidth, winheight, window, clipsize, clipside, stepsize, lrate, ylim, hdr_input, start, filterorder, filter, notchquality, notch, app, win, timeplot, curve, curvemax, plotnr, channr, timer, begsample, endsample
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -92,15 +81,8 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to input FieldTrip buffer")
 
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global channels, winx, winy, winwidth, winheight, window, clipsize, clipside, stepsize, lrate, ylim, timeout, hdr_input, start, filterorder, filter, notchquality, notch, app, win, timeplot, curve, curvemax, plotnr, channr, timer, begsample, endsample
-
-    # read variables from ini/redis
+    # get the options from the configuration file
+    debug       = patch.getint('general', 'debug', default=1)
     channels    = patch.getint('arguments', 'channels', multiple=True)
     winx        = patch.getfloat('display', 'xpos')
     winy        = patch.getfloat('display', 'ypos')
@@ -193,8 +175,8 @@ def _loop_once():
     '''Update the main figure once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global channels, winx, winy, winwidth, winheight, window, clipsize, clipside, stepsize, lrate, ylim, timeout, hdr_input, start, filterorder, filter, notchquality, notch, app, win, timeplot, curve, curvemax, plotnr, channr, timer, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, channels, winx, winy, winwidth, winheight, window, clipsize, clipside, stepsize, lrate, ylim, hdr_input, start, filterorder, filter, notchquality, notch, app, win, timeplot, curve, curvemax, plotnr, channr, timer, begsample, endsample
     global dat, timeaxis
 
     monitor.loop()
diff --git a/module/plotspectral/plotspectral.py b/module/plotspectral/plotspectral.py
index 6253ead4..3c942cb1 100755
--- a/module/plotspectral/plotspectral.py
+++ b/module/plotspectral/plotspectral.py
@@ -21,9 +21,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from pyqtgraph.Qt import QtGui, QtCore
-import configparser
-import redis
-import argparse
 import numpy as np
 import os
 import pyqtgraph as pg
@@ -59,26 +56,21 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor, ft_host, ft_port, ft_input
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channels, window, output, clipsize, clipside, stepsize, historysize, lrate, ylim, scale_red, scale_blue, offset_red, offset_blue, winx, winy, winwidth, winheight, prefix, numhistory, freqaxis, history, showred, showblue, filterorder, filter, notchquality, notch, freqrange, app, win, text_redleft_curr, text_redright_curr, text_blueleft_curr, text_blueright_curr, text_redleft_hist, text_redright_hist, text_blueleft_hist, text_blueright_hist, freqplot_curr, freqplot_hist, spect_curr, spect_hist, redleft_curr, redright_curr, blueleft_curr, blueright_curr, redleft_hist, redright_hist, blueleft_hist, blueright_hist, fft_curr, fft_hist, specmax_curr, specmin_curr, specmax_hist, specmin_hist, plotnr, channr, timer, begsample, endsample, taper
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -90,14 +82,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to input FieldTrip buffer")
 
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, channels, window, output, clipsize, clipside, stepsize, historysize, lrate, ylim, scale_red, scale_blue, offset_red, offset_blue, winx, winy, winwidth, winheight, prefix, numhistory, freqaxis, history, showred, showblue, filterorder, filter, notchquality, notch, freqrange, app, win, text_redleft_curr, text_redright_curr, text_blueleft_curr, text_blueright_curr, text_redleft_hist, text_redright_hist, text_blueleft_hist, text_blueright_hist, freqplot_curr, freqplot_hist, spect_curr, spect_hist, redleft_curr, redright_curr, blueleft_curr, blueright_curr, redleft_hist, redright_hist, blueleft_hist, blueright_hist, fft_curr, fft_hist, specmax_curr, specmin_curr, specmax_hist, specmin_hist, plotnr, channr, timer, begsample, endsample, taper
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
@@ -285,8 +269,8 @@ def _loop_once():
     '''Update the main figure once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, channels, window, output, clipsize, clipside, stepsize, historysize, lrate, ylim, scale_red, scale_blue, offset_red, offset_blue, winx, winy, winwidth, winheight, prefix, numhistory, freqaxis, history, showred, showblue, filterorder, filter, notchquality, notch, app, win, text_redleft_curr, text_redright_curr, text_blueleft_curr, text_blueright_curr, text_redleft_hist, text_redright_hist, text_blueleft_hist, text_blueright_hist, freqplot_curr, freqplot_hist, spect_curr, spect_hist, redleft_curr, redright_curr, blueleft_curr, blueright_curr, redleft_hist, redright_hist, blueleft_hist, blueright_hist, fft_curr, fft_hist, specmax_curr, specmin_curr, specmax_hist, specmin_hist, plotnr, channr, timer, begsample, endsample, taper
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channels, window, output, clipsize, clipside, stepsize, historysize, lrate, ylim, scale_red, scale_blue, offset_red, offset_blue, winx, winy, winwidth, winheight, prefix, numhistory, freqaxis, history, showred, showblue, filterorder, filter, notchquality, notch, app, win, text_redleft_curr, text_redright_curr, text_blueleft_curr, text_blueright_curr, text_redleft_hist, text_redright_hist, text_blueleft_hist, text_blueright_hist, freqplot_curr, freqplot_hist, spect_curr, spect_hist, redleft_curr, redright_curr, blueleft_curr, blueright_curr, redleft_hist, redright_hist, blueleft_hist, blueright_hist, fft_curr, fft_hist, specmax_curr, specmin_curr, specmax_hist, specmin_hist, plotnr, channr, timer, begsample, endsample, taper
     global dat, arguments_freqrange, freqrange, redfreq, redwidth, bluefreq, bluewidth
 
     monitor.loop()
diff --git a/module/plottopo/plottopo.py b/module/plottopo/plottopo.py
index b2221c72..381e3e05 100755
--- a/module/plottopo/plottopo.py
+++ b/module/plottopo/plottopo.py
@@ -21,9 +21,6 @@
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtWidgets import QApplication, QWidget
-import configparser
-import redis
-import argparse
 import os
 import sys
 import time
@@ -144,23 +141,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -171,12 +158,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, delay, winx, winy, winwidth, winheight, input_name, input_variable, variable, app, window, timer, colormap, labelposition, labelcolor
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     delay           = patch.getfloat('general', 'delay')
     winx            = patch.getfloat('display', 'xpos')
@@ -188,7 +172,7 @@ def _start():
     labelcolor      = patch.getstring('display', 'labelcolor', default='inverse')
 
     # get the input options
-    input_name, input_variable = list(zip(*config.items('input')))
+    input_name, input_variable = list(zip(*patch.config.items('input')))
 
     for name,variable in zip(input_name, input_variable):
         monitor.info("%s = %s" % (name, variable))
diff --git a/module/plottrigger/plottrigger.py b/module/plottrigger/plottrigger.py
index 32c5cc9a..74902f5a 100755
--- a/module/plottrigger/plottrigger.py
+++ b/module/plottrigger/plottrigger.py
@@ -20,9 +20,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from pyqtgraph.Qt import QtGui, QtCore
-import configparser
-import redis
-import argparse
 import numpy as np
 import os
 import pyqtgraph as pg
@@ -62,7 +59,7 @@ def __init__(self, redischannel, number):
     def stop(self):
         self.running = False
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('PLOTTRIGGER_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)      # this message contains the note
         while self.running:
@@ -70,7 +67,7 @@ def run(self):
                 if not self.running or not item['type'] == 'message':
                     break
                 if item['channel']==self.redischannel:
-                    monitor.info(item)
+                    monitor.debug(item)
                     now = time.time()
                     val = float(item['data'])
                     with lock:
@@ -82,23 +79,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -109,14 +96,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, delay, window, value, winx, winy, winwidth, winheight, data, lock, trigger, number, i, this, thread, app, win, plot
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+    global patch, name, path, monitor
+    global debug, delay, window, value, winx, winy, winwidth, winheight, data, lock, trigger, number, i, this, thread, app, win, timer, plot
 
     # get the options from the configuration file
-    debug       = patch.getint('general', 'debug')
+    debug       = patch.getint('general', 'debug', default=1)
     delay       = patch.getfloat('general', 'delay')            # in seconds
     window      = patch.getfloat('general', 'window')           # in seconds
     value       = patch.getint('general', 'value', default=0)   # boolean
@@ -133,14 +117,14 @@ def _start():
 
     trigger = []
     number = []
-    # each of the gates that can be triggered is mapped onto a different message
+    # each of the gates is mapped onto a different message
     for i in range(1, 17):
         name = 'channel{}'.format(i)
-        if config.has_option('gate', name):
+        if patch.config.has_option('gate', name):
             number.append(i)
             data[i] = []
             # start the background thread that deals with this channel
-            this = TriggerThread(patch.getstring('gate', name), i)
+            this = TriggerThread(patch.get('gate', name), i)
             trigger.append(this)
             monitor.info(name + ' trigger configured')
     if len(trigger)==0:
@@ -165,17 +149,21 @@ def _start():
     plot.setXRange(-window, 0)
     plot.setYRange(0.5, len(trigger)+0.5)
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
+    signal.signal(signal.SIGINT, _stop)
+
+    # Set timer for update
+    timer = QtCore.QTimer()
+    timer.timeout.connect(_loop_once)
+    timer.setInterval(10)                     # timeout in milliseconds
+    timer.start(int(round(delay * 1000)))     # in milliseconds
 
 
 def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, delay, window, value, winx, winy, winwidth, winheight, data, lock, trigger, number, i, this, thread, app, win, plot
+    global patch, name, path, monitor
+    global debug, delay, window, value, winx, winy, winwidth, winheight, data, lock, trigger, number, i, this, thread, app, win, timer, plot
 
     monitor.loop()
 
@@ -209,18 +197,6 @@ def _loop_once():
                 text.setPos(x, y)
                 plot.addItem(text)
 
-    signal.signal(signal.SIGINT, _stop)
-
-    # Set timer for update
-    timer = QtCore.QTimer()
-    timer.timeout.connect(_loop_once)
-    timer.setInterval(10)                     # timeout in milliseconds
-    timer.start(int(round(delay * 1000)))     # in milliseconds
-
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
 
 def _loop_forever():
     '''Run the main loop forever
@@ -235,7 +211,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('PLOTTRIGGER_UNBLOCK', 1)
+    patch.publish('PLOTTRIGGER_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     QtGui.QApplication.quit()
diff --git a/module/polarbelt/polarbelt.py b/module/polarbelt/polarbelt.py
index 79d55c12..5e219205 100755
--- a/module/polarbelt/polarbelt.py
+++ b/module/polarbelt/polarbelt.py
@@ -17,11 +17,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-
-import configparser
-import argparse
 import os
-import redis
 import sys
 import asyncio
 from bleak import BleakClient, discover
@@ -48,41 +44,18 @@
 import EEGsynth
 
 
-
 class PolarClient:
-
     def __init__(self, path, name):
-        # Configuration.
-        parser = argparse.ArgumentParser()
-        parser.add_argument("-i", "--inifile",
-                            default=os.path.join(path, name + '.ini'),
-                            help="name of the configuration file")
-        args = parser.parse_args()
-
-        config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-        config.read(args.inifile)
-
-        # Redis.
-        try:
-            rds = redis.StrictRedis(host=config.get('redis', 'hostname'),
-                                    port=config.getint('redis', 'port'),
-                                    db=0, charset='utf-8',
-                                    decode_responses=True)
-        except redis.ConnectionError as e:
-            raise RuntimeError(e)
-
-        # Combine the patching from the configuration file and Redis.
-        self.patch = EEGsynth.patch(config, rds)
+
+        # configure and start the patch, this will parse the command-line arguments and the ini file
+        patch = EEGsynth.patch(name=name, path=path)
         
+        # this shows the splash screen and can be used to track parameters that have changed
+        self.monitor = EEGsynth.monitor(name=name, debug=self.patch.getint("general", "debug", default=1))
+
         # BLE client.
         self.loop = asyncio.get_event_loop()
-        
-        self.ble_client = BleakClient(self.patch.getstring("input", "uuid"),
-                                      loop=self.loop)
-
-        self.monitor = EEGsynth.monitor(name=name,
-                                        debug=self.patch.getint("general", "debug"))
-
+        self.ble_client = BleakClient(self.patch.getstring("input", "uuid"), loop=self.loop)
         self.loop.run_until_complete(self.discover())
 
 
diff --git a/module/postprocessing/postprocessing.py b/module/postprocessing/postprocessing.py
index fd32a42d..cce48fcf 100755
--- a/module/postprocessing/postprocessing.py
+++ b/module/postprocessing/postprocessing.py
@@ -20,11 +20,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from numpy import log, log2, log10, exp, power, sqrt, mean, median, var, std, mod, random
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -82,23 +79,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -109,26 +96,23 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, input_name, input_variable, output_name, output_equation, variable, equation
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
-    if 'initial' in config.sections():
+    if 'initial' in patch.config.sections():
         # assign the initial values
-        for item in config.items('initial'):
-            val = patch.getfloat('initial', item[0])
+        for item in patch.config.items('initial'):
+            val = patch.getfloat('initial', item[0], default=np.nan)
             patch.setvalue(item[0], val)
             monitor.update(item[0], val)
 
     # get the input and output options
-    if len(config.items('input')):
-        input_name, input_variable = list(zip(*config.items('input')))
+    if len(patch.config.items('input')):
+        input_name, input_variable = list(zip(*patch.config.items('input')))
     else:
         input_name, input_variable = ([], [])
-    if len(config.items('output')):
-        output_name, output_equation = list(zip(*config.items('output')))
+    if len(patch.config.items('output')):
+        output_name, output_equation = list(zip(*patch.config.items('output')))
     else:
         output_name, output_equation = ([], [])
 
@@ -152,14 +136,14 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, input_name, input_variable, output_name, output_equation, variable, equation
 
     monitor.debug('============================')
 
     input_value = []
     for name in input_name:
-        val = patch.getfloat('input', name)
+        val = patch.getfloat('input', name, default=np.nan)
         input_value.append(val)
 
     for key, equation in zip(output_name, output_equation):
@@ -172,6 +156,8 @@ def _loop_once():
 
         # try to evaluate the equation
         try:
+            equation = equation.replace('nan', 'np.nan')
+            equation = equation.replace('inf', 'np.inf')
             val = eval(equation)
             val = float(val) # deal with True/False
             monitor.debug('%s = %s = %g' % (key, equation, val))
diff --git a/module/preprocessing/preprocessing.ini b/module/preprocessing/preprocessing.ini
index d65203ae..0620144b 100644
--- a/module/preprocessing/preprocessing.ini
+++ b/module/preprocessing/preprocessing.ini
@@ -1,6 +1,6 @@
 [general]
 delay=0.05
-debug=3
+debug=1
 
 [redis]
 hostname=localhost
diff --git a/module/preprocessing/preprocessing.py b/module/preprocessing/preprocessing.py
index fbfbdb99..21fbadf8 100755
--- a/module/preprocessing/preprocessing.py
+++ b/module/preprocessing/preprocessing.py
@@ -20,9 +20,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import redis
-import argparse
 import os
 import sys
 import time
@@ -52,30 +49,27 @@
 import EEGsynth
 import FieldTrip
 
+
 def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    global patch, name, path, monitor
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, ft_output, timeout, hdr_input, start, window, downsample, differentiate, integrate, rectify, smoothing, reference, default_scale, scale_lowpass, scale_highpass, scale_notchfilter, offset_lowpass, offset_highpass, offset_notchfilter, scale_filterorder, scale_notchquality, offset_filterorder, offset_notchquality, previous, differentiate_zi, integrate_zi, begsample, endsample
+    global montage_in, montage_out
 
     try:
         ft_host = patch.getstring('input_fieldtrip','hostname')
@@ -97,15 +91,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to output FieldTrip buffer")
 
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output, name
-    global timeout, hdr_input, start, window, downsample, differentiate, integrate, rectify, smoothing, reference, default_scale, scale_lowpass, scale_highpass, scale_notchfilter, offset_lowpass, offset_highpass, offset_notchfilter, scale_filterorder, scale_notchquality, offset_filterorder, offset_notchquality, previous, differentiate_zi, integrate_zi, begsample, endsample
-    global montage_in, montage_out
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('input_fieldtrip', 'timeout', default=30)
 
@@ -135,7 +120,7 @@ def _start():
     reference       = patch.getstring('processing', 'reference')
 
     if reference == 'montage':
-        montage_out, montage_in = list(map(list, list(zip(*config.items('montage')))))
+        montage_out, montage_in = list(map(list, list(zip(*patch.config.items('montage')))))
         nChannels_out = len(montage_out)
         # construct the montage matrix once to give feedback, it will later be recomputed on the fly
         for i, name in enumerate(montage_out):
@@ -146,9 +131,9 @@ def _start():
         nChannels_out = hdr_input.nChannels
 
     try:
-        float(config.get('processing', 'highpassfilter'))
-        float(config.get('processing', 'lowpassfilter'))
-        float(config.get('processing', 'notchfilter'))
+        float(patch.config.get('processing', 'highpassfilter'))
+        float(patch.config.get('processing', 'lowpassfilter'))
+        float(patch.config.get('processing', 'notchfilter'))
         # the filter frequencies are specified as numbers, assume they are in Hz
         default_scale = 1.
     except:
@@ -194,8 +179,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output
-    global timeout, hdr_input, start, window, downsample, differentiate, integrate, rectify, smoothing, reference, default_scale, scale_lowpass, scale_highpass, scale_notchfilter, offset_lowpass, offset_highpass, offset_notchfilter, scale_filterorder, scale_notchquality, offset_filterorder, offset_notchquality, previous, differentiate_zi, integrate_zi, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, ft_output, timeout, hdr_input, start, window, downsample, differentiate, integrate, rectify, smoothing, reference, default_scale, scale_lowpass, scale_highpass, scale_notchfilter, offset_lowpass, offset_highpass, offset_notchfilter, scale_filterorder, scale_notchquality, offset_filterorder, offset_notchquality, previous, differentiate_zi, integrate_zi, begsample, endsample
     global dat_input, dat_output, highpassfilter, lowpassfilter, filterorder, change, b, a, zi, notchfilter, notchquality, nb, na, nzi, window_new, t
     global montage_in, montage_out
 
@@ -332,7 +317,6 @@ def _stop():
     '''Stop and clean up on SystemExit, KeyboardInterrupt
     '''
     global monitor, ft_input, ft_output
-
     ft_input.disconnect()
     monitor.success('Disconnected from input FieldTrip buffer')
     ft_output.disconnect()
diff --git a/module/processtrigger/processtrigger.py b/module/processtrigger/processtrigger.py
index ef8870db..54455a52 100755
--- a/module/processtrigger/processtrigger.py
+++ b/module/processtrigger/processtrigger.py
@@ -21,11 +21,8 @@
 
 from numpy import log, log2, log10, exp, power, sqrt, mean, median, var, std, mod
 from numpy import random
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 import threading
@@ -91,7 +88,7 @@ def stop(self):
 
     def run(self):
         global r, monitor, patch
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('PROCESSTRIGGER_UNBLOCK')  # this message unblocks the Redis listen command
         pubsub.subscribe(self.redischannel)        # this message triggers the event
         while self.running:
@@ -150,23 +147,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -177,40 +164,37 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, prefix, item, val, input_name, input_variable, output_name, output_equation, variable, equation, lock, trigger, thread
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     prefix = patch.getstring('output', 'prefix')
 
-    if 'initial' in config.sections():
+    if 'initial' in patch.config.sections():
         # assign the initial values
-        for item in config.items('initial'):
+        for item in patch.config.items('initial'):
             val = patch.getfloat('initial', item[0])
             patch.setvalue(item[0], val)
             monitor.update(item[0], val)
 
     # get the input variables
-    if len(config.items('input')):
-        input_name, input_variable = list(zip(*config.items('input')))
+    if len(patch.config.items('input')):
+        input_name, input_variable = list(zip(*patch.config.items('input')))
     else:
         input_name, input_variable = ([], [])
 
     # get the output equations for each trigger
     output_name = {}
     output_equation = {}
-    for item in config.items('trigger'):
-        output_name[item[0]], output_equation[item[0]] = list(zip(*config.items(item[0])))
+    for item in patch.config.items('trigger'):
+        output_name[item[0]], output_equation[item[0]] = list(zip(*patch.config.items(item[0])))
         # make the equations robust against sub-string replacements
         output_equation[item[0]] = [sanitize(equation) for equation in output_equation[item[0]]]
 
     monitor.info('===== input variables =====')
     for name, variable in zip(input_name, input_variable):
         monitor.info(name + ' = ' + variable)
-    for item in config.items('trigger'):
+    for item in patch.config.items('trigger'):
         monitor.info('===== output equations for %s =====' % item[0])
         for name, equation in zip(output_name[item[0]], output_equation[item[0]]):
             monitor.info(name + ' = ' + equation)
@@ -222,7 +206,7 @@ def _start():
     # create the background threads that deal with the triggers
     trigger = []
     monitor.debug("Setting up threads for each trigger")
-    for item in config.items('trigger'):
+    for item in patch.config.items('trigger'):
         trigger.append(TriggerThread(item[1], item[0]))
         monitor.debug(item[0] + " " + item[1] + " OK")
 
@@ -256,7 +240,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('PROCESSTRIGGER_UNBLOCK', 1)
+    patch.publish('PROCESSTRIGGER_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
@@ -268,4 +252,4 @@ def _stop(*args):
     try:
         _loop_forever()
     except (SystemExit, KeyboardInterrupt, RuntimeError):
-        _stop()
\ No newline at end of file
+        _stop()
diff --git a/module/quantizer/quantizer.py b/module/quantizer/quantizer.py
index 56240f93..25c374d4 100755
--- a/module/quantizer/quantizer.py
+++ b/module/quantizer/quantizer.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -59,23 +56,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -86,14 +73,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, input_scale, input_offset, output_scale, output_offset, input_channel, input_name, output_name, output_value, index, input_value
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, input_scale, input_offset, output_scale, output_offset, input_channel, input_name, output_name, output_value, index, input_value
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # the input scale and offset are used to map Redis values to internal values
     input_scale = patch.getfloat('input', 'scale', default=127)
@@ -103,8 +87,8 @@ def _start():
     output_offset = patch.getfloat('output', 'offset', default=0)
 
     # get the input and output options
-    input_channel, input_name = list(map(list, list(zip(*config.items('input')))))
-    output_name, output_value = list(map(list, list(zip(*config.items('quantization')))))
+    input_channel, input_name = list(map(list, list(zip(*patch.config.items('input')))))
+    output_name, output_value = list(map(list, list(zip(*patch.config.items('quantization')))))
 
     # remove the scale and offset from the input list
     del input_channel[input_channel.index('scale')]
@@ -121,18 +105,13 @@ def _start():
     del output_name[index]
     del output_value[index]
 
-    # there should not be any local variables in this function, they should all be global
-    del i, name
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
 
 def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, input_scale, input_offset, output_scale, output_offset, input_channel, input_name, output_name, output_value, i, index, input_value
+    global patch, name, path, monitor
+    global debug, input_scale, input_offset, output_scale, output_offset, input_channel, input_name, output_name, output_value, i, index, input_value
     global channel, val, idx, qname, qvalue, key
 
     monitor.info('----------------------------------------')
@@ -161,10 +140,6 @@ def _loop_once():
                 val = EEGsynth.rescale(val, slope=output_scale, offset=output_offset)
                 patch.setvalue(key, val)
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
 
 def _loop_forever():
     '''Run the main loop forever
diff --git a/module/recordcontrol/recordcontrol.py b/module/recordcontrol/recordcontrol.py
index 7ca3f29b..611c125d 100755
--- a/module/recordcontrol/recordcontrol.py
+++ b/module/recordcontrol/recordcontrol.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import datetime
 import numpy as np
 import os
-import redis
 import sys
 import time
 import wave
@@ -56,23 +53,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -83,11 +70,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, MININT16, MAXINT16, MININT32, MAXINT32, debug, delay, filename, fileformat, filenumber, recording, adjust
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global MININT16, MAXINT16, MININT32, MAXINT32, debug, delay, filename, fileformat, filenumber, recording, adjust
 
     MININT16 = -np.power(2., 15)
     MAXINT16 = np.power(2., 15) - 1
@@ -95,7 +79,7 @@ def _start():
     MAXINT32 = np.power(2., 31) - 1
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     delay = patch.getfloat('general', 'delay')
     filename = patch.getstring('recording', 'file')
     fileformat = patch.getstring('recording', 'format')
@@ -118,8 +102,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, MININT16, MAXINT16, MININT32, MAXINT32, debug, delay, filename, fileformat, filenumber, recording, adjust
+    global patch, name, path, monitor
+    global MININT16, MAXINT16, MININT32, MAXINT32, debug, delay, filename, fileformat, filenumber, recording, adjust
     global start, fname, f, ext, blocksize, synchronize, channels, channelz, nchans, sample, replace, i, s, z, physical_min, physical_max, meas_info, chan_info, recstart, D, chan, xval, elapsed
 
     # measure the time to correct for the slip
@@ -155,7 +139,7 @@ def _loop_once():
         sample = 0
 
         # search-and-replace to reduce the length of the channel labels
-        for replace in config.items('replace'):
+        for replace in patch.config.items('replace'):
             monitor.debug(replace)
             for i in range(len(channelz)):
                 channelz[i] = channelz[i].replace(replace[0], replace[1])
@@ -201,10 +185,9 @@ def _loop_once():
     if recording:
         D = []
         for chan in channels:
-            xval = r.get(chan)
             try:
-                xval = float(xval)
-            except ValueError:
+                xval = float(patch.redis.get(chan))
+            except:
                 xval = 0.
             xval = EEGsynth.limit(xval, physical_min, physical_max)
             D.append([xval])
@@ -244,7 +227,7 @@ def _loop_once():
 def _loop_forever():
     '''Run the main loop forever
     '''
-    global monitor, patch
+    global monitor
     while True:
         monitor.loop()
         _loop_once()
diff --git a/module/recordsignal/recordsignal.py b/module/recordsignal/recordsignal.py
index 4452bc2a..572da77b 100755
--- a/module/recordsignal/recordsignal.py
+++ b/module/recordsignal/recordsignal.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import datetime
 import numpy as np
 import os
-import redis
 import sys
 import time
 import wave
@@ -58,23 +55,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -85,11 +72,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, MININT16, MAXINT16, MININT32, MAXINT32, debug, timeout, filename, fileformat, ft_host, ft_port, ft_input, hdr_input, start, recording
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global MININT16, MAXINT16, MININT32, MAXINT32, debug, timeout, filename, fileformat, ft_host, ft_port, ft_input, hdr_input, start, recording
 
     MININT16 = -np.power(2, 15)
     MAXINT16 = np.power(2, 15) - 1
@@ -97,7 +81,7 @@ def _start():
     MAXINT32 = np.power(2., 31) - 1
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
     filename = patch.getstring('recording', 'file')
     fileformat = patch.getstring('recording', 'format')
@@ -141,8 +125,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, MININT16, MAXINT16, MININT32, MAXINT32, debug, timeout, filename, fileformat, ft_host, ft_port, ft_input, hdr_input, start, recording
+    global patch, name, path, monitor
+    global MININT16, MAXINT16, MININT32, MAXINT32, debug, timeout, filename, fileformat, ft_host, ft_port, ft_input, hdr_input, start, recording
     global fname, f, ext, blocksize, synchronize, physical_min, physical_max, meas_info, chan_info, now, begsample, endsample, startsample, dat, key
 
     hdr_input = ft_input.getHeader()
@@ -265,7 +249,7 @@ def _loop_once():
 def _loop_forever():
     '''Run the main loop forever
     '''
-    global monitor, patch
+    global monitor
     while True:
         monitor.loop()
         _loop_once()
@@ -274,6 +258,9 @@ def _loop_forever():
 def _stop(*args):
     '''Stop and clean up on SystemExit, KeyboardInterrupt
     '''
+    global monitor, ft_input
+    ft_input.disconnect()
+    monitor.success('Disconnected from input FieldTrip buffer')
     sys.exit()
 
 
diff --git a/module/recordtrigger/recordtrigger.py b/module/recordtrigger/recordtrigger.py
index f83032cc..cc6deede 100755
--- a/module/recordtrigger/recordtrigger.py
+++ b/module/recordtrigger/recordtrigger.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import datetime
 import os
-import redis
 import sys
 import time
 import threading
@@ -62,7 +59,7 @@ def stop(self):
 
     def run(self):
         global r, monitor, lock
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('RECORDTRIGGER_UNBLOCK')  # this message unblocks the Redis listen command
         pubsub.subscribe(self.redischannel)        # this message triggers the event
         while self.running:
@@ -92,24 +89,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(parser, preservecase=True)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.optionxform = str
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -120,14 +106,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, delay, input_scale, input_offset, filename, fileformat, f, recording, filenumber, lock, trigger, item, thread
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, delay, input_scale, input_offset, filename, fileformat, f, recording, filenumber, lock, trigger, item, thread
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     delay = patch.getfloat('general', 'delay')
     input_scale = patch.getfloat('input', 'scale', default=None)
     input_offset = patch.getfloat('input', 'offset', default=None)
@@ -145,7 +128,7 @@ def _start():
     # create the background threads that deal with the triggers
     trigger = []
     monitor.info("Setting up threads for each trigger")
-    for item in config.items('trigger'):
+    for item in patch.config.items('trigger'):
         trigger.append(TriggerThread(item[0]))
         monitor.debug(item[0] + ' = OK')
 
@@ -162,8 +145,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, delay, input_scale, input_offset, filename, fileformat, f, recording, filenumber, lock, trigger, item, thread
+    global patch, name, path, monitor
+    global debug, delay, input_scale, input_offset, filename, fileformat, f, recording, filenumber, lock, trigger, item, thread
     global fname, ext
 
     if recording and not patch.getint('recording', 'record'):
@@ -213,7 +196,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('RECORDTRIGGER_UNBLOCK', 1)
+    patch.publish('RECORDTRIGGER_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/redis/__init__.py b/module/redis/__init__.py
new file mode 100644
index 00000000..9183b614
--- /dev/null
+++ b/module/redis/__init__.py
@@ -0,0 +1,24 @@
+import sys
+import time
+
+from .redis import _setup, _start, _loop_once, _loop_forever, _stop
+
+class Executable:
+    def __init__(self, args=None):
+        if args!=None:
+            # override the command line arguments
+            sys.argv = [sys.argv[0]] + args
+
+        # the setup MUST pass without errors
+        _setup()
+
+        while True:
+            # keep running until KeyboardInterrupt
+            try:
+                _start()
+                _loop_forever()
+            except RuntimeError:
+                # do not restart
+                raise SystemExit
+            except KeyboardInterrupt:
+                raise SystemExit
diff --git a/module/redis/redis.ini b/module/redis/redis.ini
new file mode 100644
index 00000000..dad4b5d8
--- /dev/null
+++ b/module/redis/redis.ini
@@ -0,0 +1,11 @@
+[general]
+debug=1
+broker=redis
+
+[redis]
+hostname=localhost
+port=6379
+
+[zeromq]
+hostname=localhost
+port=5555
diff --git a/module/redis/redis.py b/module/redis/redis.py
new file mode 100755
index 00000000..f2ccb413
--- /dev/null
+++ b/module/redis/redis.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# This module emulates a Redis server in pure Python using ZeroMQ
+#
+# This software is part of the EEGsynth project, see <https://github.com/eegsynth/eegsynth>.
+#
+# Copyright (C) 2023 EEGsynth project
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+if hasattr(sys, 'frozen'):
+    path = os.path.split(sys.executable)[0]
+    file = os.path.split(sys.executable)[-1]
+    name = os.path.splitext(file)[0]
+elif __name__=='__main__' and sys.argv[0] != '':
+    path = os.path.split(sys.argv[0])[0]
+    file = os.path.split(sys.argv[0])[-1]
+    name = os.path.splitext(file)[0]
+elif __name__=='__main__':
+    path = os.path.abspath('')
+    file = os.path.split(path)[-1] + '.py'
+    name = os.path.splitext(file)[0]
+else:
+    path = os.path.split(__file__)[0]
+    file = os.path.split(__file__)[-1]
+    name = os.path.splitext(file)[0]
+
+# eegsynth/lib contains shared modules
+sys.path.insert(0, os.path.join(path, '../../lib'))
+import EEGsynth
+import ZmqRedis
+
+
+def _setup():
+    '''Initialize the module
+    '''
+    global patch, name, path, monitor
+
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
+
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
+
+
+def _start():
+    '''Start the module
+    '''
+    global patch, name, path, monitor
+    global monitor, broker, server
+
+    # get the options from the configuration file
+    broker = patch.get('general', 'broker', default='zeromq')
+    
+    if broker=='zeromq':
+        monitor.success('starting the zeromq broker')
+        port = patch.getint('zeromq', 'port', default=5555)
+        server = ZmqRedis.server(port=port)
+
+    elif broker=='redis':
+        msg = 'the Redis broker should be started using "redis.sh" or by calling it directly'
+        monitor.error(msg)
+        raise RuntimeError(msg)
+        
+    elif broker=='fake':
+        msg = 'the fake broker does not require a server'
+        monitor.error(msg)
+        raise RuntimeError(msg)
+
+    elif broker=='dummy':
+        msg = 'the dummy broker does not require a server'
+        monitor.error(msg)
+        raise RuntimeError(msg)
+        
+    else:
+        msg = 'unknown broker'
+        monitor.error(msg)
+        raise RuntimeError(msg)
+
+
+def _loop_once():
+    '''Run the main loop once
+    '''
+    pass
+
+
+def _loop_forever():
+    '''Run the main loop forever
+    '''
+    global monitor, broker, server
+    if broker=='zeromq':
+        server.start()
+
+
+def _stop():
+    '''Stop and clean up on SystemExit, KeyboardInterrupt
+    '''
+    pass
+
+
+if __name__ == '__main__':
+    _setup()
+    _start()
+    try:
+        _loop_forever()
+    except (SystemExit, KeyboardInterrupt, RuntimeError):
+        _stop()
diff --git a/module/redis/redis.sh b/module/redis/redis.sh
index 460e97cd..440bdd0e 100755
--- a/module/redis/redis.sh
+++ b/module/redis/redis.sh
@@ -4,7 +4,7 @@
 # in the specified or the default inifile.
 #
 # Use as
-#   redis.sh [-i <inifile>] [-h] [-v]
+#   redis.sh [-h] [-v] [-d] [-i <inifile>]
 
 # include library with helper functions
 . "$(dirname "$0")/../../lib/EEGsynth.sh"
@@ -14,6 +14,7 @@ NAME=`basename "$0" .sh`
 BINDIR=$DIR/../../bin
 
 # set the default
+INIFILE=${DIR}/${NAME}.ini
 VERBOSE=notice
 
 if [ -e "/usr/bin/redis-server" ]; then
@@ -26,30 +27,38 @@ else
   CONFIG=`echo $COMMAND | sed s/bin/etc/g | sed s/-server/\.conf/g`
 fi
 
-while getopts "hvd" option; do
-  case "${option}" in
-    d)
-      VERBOSE=debug
+# debug (a lot of information, useful for development/testing)
+# verbose (many rarely useful info, but not a mess like the debug level)
+# notice (moderately verbose, what you want in production probably)
+# warning (only very important / critical messages are logged)
+
+while getopts "hvdi:" OPTION; do
+  case ${OPTION} in
+    h)
+      echo "Use as: $0 [-h] [-v] [-d] [-i <inifile>]"
+      exit 0
       ;;
     v)
       VERBOSE=verbose
       ;;
-    h)
-      echo "Use as: $0 [-h] [-v]"
+    d)
+      VERBOSE=debug
+      ;;
+    i)
+      INIFILE=${OPTARG}
       ;;
     \?)
-      echo "Invalid option: -${OPTARG}" >&2
+      exit 0
       ;;
   esac
 done
 
-# debug (a lot of information, useful for development/testing)
-# verbose (many rarely useful info, but not a mess like the debug level)
-# notice (moderately verbose, what you want in production probably)
-# warning (only very important / critical messages are logged)
+# this parses the ini file and creates local variables
+shini_parse $INIFILE
+PORT=$ini_redis_port
 
 echo COMMAND=$COMMAND
 echo CONFIG=$CONFIG
 echo VERBOSE=$VERBOSE
 
-${COMMAND} ${CONFIG} --loglevel $VERBOSE --protected-mode no
+${COMMAND} ${CONFIG} --port $PORT --loglevel $VERBOSE --protected-mode no
diff --git a/module/rms/rms.py b/module/rms/rms.py
index bcc1c720..15d945f6 100755
--- a/module/rms/rms.py
+++ b/module/rms/rms.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import math
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -55,29 +52,25 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # there should not be any local variables in this function, they should all be global
+    if len(locals()):
+        print('LOCALS: ' + ', '.join(locals().keys()))
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
 
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, window, begsample, endsample
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -89,18 +82,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to FieldTrip buffer")
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, window, begsample, endsample
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
@@ -117,7 +98,7 @@ def _start():
     monitor.debug(hdr_input)
     monitor.debug(hdr_input.labels)
 
-    channel_items = config.items('input')
+    channel_items = patch.config.items('input')
     channame = []
     chanindx = []
     for item in channel_items:
@@ -141,8 +122,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, window, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, window, begsample, endsample
     global dat, rms, i, chanvec, chanval, name, val, key
 
     hdr_input = ft_input.getHeader()
diff --git a/module/sampler/sampler.py b/module/sampler/sampler.py
index 30d47c06..20cab6e1 100755
--- a/module/sampler/sampler.py
+++ b/module/sampler/sampler.py
@@ -19,13 +19,10 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import scipy.io
 from scipy.io import wavfile
 import os
-import redis
 import sys
 import time
 import pyaudio
@@ -94,7 +91,7 @@ def stop(self):
 
     def run(self):
         global r, monitor, patch, stack, current_channel, current_value
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('SAMPLER_UNBLOCK')  # this message unblocks the Redis listen command
         pubsub.subscribe(self.redischannel)  # this message triggers the event
         while self.running:
@@ -201,23 +198,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -228,14 +215,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, device, scaling_method, scaling, speed, onset, offset, taper, scale_scaling, scale_speed, scale_onset, scale_offset, scale_taper, offset_scaling, offset_speed, offset_onset, offset_offset, offset_taper, started, finished, p, info, i, devinfo, lock, input_channel, input_sample, rate, dat, channels, stack, current_channel, current_value, trigger, channel, sample, thread, stream
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global debug, device, scaling_method, scaling, speed, onset, offset, taper, scale_scaling, scale_speed, scale_onset, scale_offset, scale_taper, offset_scaling, offset_speed, offset_onset, offset_offset, offset_taper, started, finished, p, info, i, devinfo, lock, input_channel, input_sample, rate, dat, channels, stack, current_channel, current_value, trigger, channel, sample, thread, stream
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
     device = patch.getint('audio', 'device')
     scaling_method = patch.getstring('audio', 'scaling_method')
     scaling = patch.getfloat('audio', 'scaling', default=1)
@@ -276,7 +260,7 @@ def _start():
     # this is to prevent concurrency problems
     lock = threading.Lock()
 
-    input_channel, input_sample = list(zip(*config.items('input')))
+    input_channel, input_sample = list(zip(*patch.config.items('input')))
     input_sample = [x.split(',') for x in input_sample]
 
     # open first file to determine the format
@@ -327,7 +311,7 @@ def _loop_once():
 def _loop_forever():
     '''Run the main loop forever
     '''
-    global monitor, patch
+    global monitor
     while True:
         monitor.loop()
         _loop_once()
@@ -344,7 +328,7 @@ def _stop(*args):
     monitor.success("Closing threads")
     for thread in trigger:
         thread.stop()
-    r.publish('SAMPLER_UNBLOCK', 1)
+    patch.publish('SAMPLER_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/sequencer/sequencer.py b/module/sequencer/sequencer.py
index f90feb95..d0cc5f80 100755
--- a/module/sequencer/sequencer.py
+++ b/module/sequencer/sequencer.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import math
 import numpy as np
 import os
-import redis
 import sys
 import threading
 import time
@@ -81,7 +78,7 @@ def stop(self):
 
     def run(self):
         global r, monitor, patch
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('SEQUENCER_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)    # this message contains the note
         while self.running:
@@ -89,6 +86,7 @@ def run(self):
                 if not self.running or not item['type'] == 'message':
                     break
                 if item['channel'] == self.redischannel:
+                    monitor.debug(item)
                     now = time.time()
                     if self.prevtime != None:
                         self.steptime = now - self.prevtime
@@ -97,26 +95,27 @@ def run(self):
                         # the sequence can consist of a list of values or a list of Redis channels
                         val = self.sequence[self.step % len(self.sequence)]
 
-                        try:
-                            # convert the string from the ini to floating point
-                            val = float(val)
-                        except:
-                            # get the value from Redis
-                            val = r.get(val)
-                            if val == None:
+                        if val[0].isalpha():
+                            # get the value directly from Redis
+                            try:
+                                val = float(patch.redis.get(val))
+                            except:
                                 val = 0.
-                            else:
-                                # convert the string from Redis to floating point
+                        else:
+                            try:
                                 val = float(val)
+                            except ValueError:
+                                val = 0.
 
                         # apply the scaling, offset and transpose the note
                         val = EEGsynth.rescale(val, slope=scale_note, offset=offset_note)
                         val += self.transpose
+                        
                         # send it as sequencer.note with the note as value
                         patch.setvalue(self.key, val, duration=self.duration * self.steptime)
                         if val >= 1.:
                             # send it also as sequencer.noteXXX with value 1.0
-                            key = '%s%03d' % (self.key, val)
+                            key = '%s%03d' % (self.key, round(val))
                             patch.setvalue(key, 1., duration=self.duration * self.steptime)
                         monitor.info("step %2d : %s = %g" % (self.step + 1, self.key, val))
                         # increment to the next step
@@ -127,24 +126,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    global patch, name, path, monitor
+    
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -155,15 +143,11 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, stepsize, clock, prefix, scale_active, scale_transpose, scale_note, scale_duration, offset_active, offset_transpose, offset_note, offset_duration, lock, key, sequencethread
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     stepsize = patch.getfloat('general', 'delay')
-    clock = patch.getstring('sequence', 'clock')  # the clock signal for the sequence
     prefix = patch.getstring('output', 'prefix')
 
     # these scale and offset parameters are used to map between Redis and internal values
@@ -179,6 +163,8 @@ def _start():
     # this is to prevent two messages from being sent at the same time
     lock = threading.Lock()
 
+    # the clock signal for the sequence
+    clock = patch.get('sequence', 'clock')
     # the notes will be sent to Redis using this key
     key = "{}.note".format(prefix)
 
@@ -204,7 +190,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, stepsize, clock, prefix, scale_active, scale_transpose, scale_note, scale_duration, offset_active, offset_transpose, offset_note, offset_duration, lock, key, sequencethread
     global active, sequence, transpose, duration, elapsed, naptime
 
@@ -245,7 +231,7 @@ def _loop_once():
 def _loop_forever():
     '''Run the main loop forever
     '''
-    global monitor, patch
+    global monitor
     while True:
         # measure the time to correct for the slip
         start = time.time()
@@ -272,7 +258,7 @@ def _stop(*args):
         pass
     monitor.success('Closing threads')
     sequencethread.stop()
-    r.publish('SEQUENCER_UNBLOCK', 1)
+    patch.publish('SEQUENCER_UNBLOCK', 1)
     sequencethread.join()
     sys.exit()
 
diff --git a/module/slewlimiter/slewlimiter.py b/module/slewlimiter/slewlimiter.py
index e89f6383..f902a3f6 100755
--- a/module/slewlimiter/slewlimiter.py
+++ b/module/slewlimiter/slewlimiter.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 
@@ -51,23 +48,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -78,17 +65,14 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, prefix, input_name, input_variable, previous_val
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     prefix = patch.getstring('output', 'prefix')
 
     # get the list of input variables
-    input_name, input_variable = list(map(list, list(zip(*config.items('input')))))
+    input_name, input_variable = list(map(list, list(zip(*patch.config.items('input')))))
 
     previous_val = {}
     for name in input_name:
@@ -103,7 +87,7 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
     global monitor, prefix, input_name, input_variable, previous_val
 
     lrate = patch.getfloat('processing', 'learning_rate', default=1)
diff --git a/module/sonification/sonification.py b/module/sonification/sonification.py
index cf643aee..067a100d 100755
--- a/module/sonification/sonification.py
+++ b/module/sonification/sonification.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -54,29 +51,25 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # there should not be any local variables in this function, they should all be global
+    if len(locals()):
+        print('LOCALS: ' + ', '.join(locals().keys()))
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
 
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, ft_output, timeout, hdr_input, start, sample_rate, f_shift, f_offset, f_order, window, sideband, left, right, scaling, scaling_method, scale_scaling, offset_scaling, default_scale, scale_lowpass, scale_highpass, offset_lowpass, offset_highpass, scale_filterorder, offset_filterorder, hdr_output, nInput, nOutput, begsample, endsample, dat_output, left_f, left_b, left_a, left_zi, right_f, right_b, right_a, right_zi, i, highpass, lowpass
 
     try:
         ft_host = patch.getstring('input_fieldtrip', 'hostname')
@@ -98,20 +91,8 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to output FieldTrip buffer")
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output, name
-    global timeout, hdr_input, start, sample_rate, f_shift, f_offset, f_order, window, sideband, left, right, scaling, scaling_method, scale_scaling, offset_scaling, default_scale, scale_lowpass, scale_highpass, offset_lowpass, offset_highpass, scale_filterorder, offset_filterorder, hdr_output, nInput, nOutput, begsample, endsample, dat_output, left_f, left_b, left_a, left_zi, right_f, right_b, right_a, right_zi, i, highpass, lowpass
-
     # this is the timeout for the FieldTrip buffer
-    timeout     = patch.getfloat('input_fieldtrip', 'timeout', default=30)
+    timeout = patch.getfloat('input_fieldtrip', 'timeout', default=30)
 
     hdr_input = None
     start = time.time()
@@ -143,8 +124,8 @@ def _start():
     offset_scaling = patch.getfloat('offset', 'scaling', default=0)
 
     try:
-        float(config.get('processing', 'highpassfilter'))
-        float(config.get('processing', 'lowpassfilter'))
+        float(patch.config.get('processing', 'highpassfilter'))
+        float(patch.config.get('processing', 'lowpassfilter'))
         # the filter frequencies are specified as numbers
         default_scale = 1.
     except:
@@ -245,8 +226,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, ft_output
-    global timeout, hdr_input, start, sample_rate, f_shift, f_offset, f_order, window, sideband, left, right, scaling, scaling_method, scale_scaling, offset_scaling, default_scale, scale_lowpass, scale_highpass, offset_lowpass, offset_highpass, scale_filterorder, offset_filterorder, hdr_output, nInput, nOutput, begsample, endsample, dat_output, left_f, left_b, left_a, left_zi, right_f, right_b, right_a, right_zi, i, highpass, lowpass
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, ft_output, timeout, hdr_input, start, sample_rate, f_shift, f_offset, f_order, window, sideband, left, right, scaling, scaling_method, scale_scaling, offset_scaling, default_scale, scale_lowpass, scale_highpass, offset_lowpass, offset_highpass, scale_filterorder, offset_filterorder, hdr_output, nInput, nOutput, begsample, endsample, dat_output, left_f, left_b, left_a, left_zi, right_f, right_b, right_a, right_zi, i, highpass, lowpass
     global dat_input, begtime, endtime, tim_input, tim_output, chan, vec_output, highpassfilter, lowpassfilter, filterorder, change, b, a, zi, duration, desired
 
     # determine when we start polling for available data
@@ -367,6 +348,9 @@ def _loop_forever():
 def _stop():
     '''Stop and clean up on SystemExit, KeyboardInterrupt
     '''
+    global monitor, ft_input
+    ft_input.disconnect()
+    monitor.success('Disconnected from input FieldTrip buffer')
     sys.exit()
 
 
diff --git a/module/spectral/spectral.py b/module/spectral/spectral.py
index b6d5c70a..8470d2ba 100755
--- a/module/spectral/spectral.py
+++ b/module/spectral/spectral.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 from scipy.signal import detrend
@@ -55,26 +52,21 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, output, begsample, endsample
 
     try:
         ft_host = patch.getstring('fieldtrip','hostname')
@@ -86,14 +78,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to FieldTrip buffer")
 
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, output, begsample, endsample
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
@@ -110,7 +94,7 @@ def _start():
     monitor.debug(hdr_input)
     monitor.debug(hdr_input.labels)
 
-    channel_items = config.items('input')
+    channel_items = patch.config.items('input')
     channame = []
     chanindx = []
     for item in channel_items:
@@ -131,8 +115,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, output, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, channel_items, channame, chanindx, item, prefix, output, begsample, endsample
     global scale_window, offset_window, window, taper, frequency, band_items, bandname, bandlo, bandhi, lohi, dat, power, chan, band, meandat, sample, F, i, lo, hi, count, key
 
     scale_window = patch.getfloat('scale', 'window', default=1.)
@@ -146,13 +130,13 @@ def _loop_once():
     taper = np.hanning(window)
     frequency = np.fft.fftfreq(window, 1.0 / hdr_input.fSample)
 
-    band_items = config.items('band')
+    band_items = patch.config.items('band')
     bandname = []
     bandlo   = []
     bandhi   = []
     for item in band_items:
         # channel numbers are one-offset in the ini file, zero-offset in the code
-        lohi = patch.getfloat('band', item[0], multiple=True)
+        lohi = patch.getfloat('band', item[0], multiple=True, default=[0,hdr_input.fSample/2])
         bandname.append(item[0])
         bandlo.append(lohi[0])
         bandhi.append(lohi[1])
diff --git a/module/synthesizer/synthesizer.py b/module/synthesizer/synthesizer.py
index 9c945a32..a04887ba 100755
--- a/module/synthesizer/synthesizer.py
+++ b/module/synthesizer/synthesizer.py
@@ -19,13 +19,10 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import math
 import multiprocessing
 import os
 import pyaudio
-import redis
 import sys
 import threading
 import time
@@ -64,7 +61,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('SYNTHESIZER_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(patch.getstring('control', 'adsr_gate'))
         while self.running:
@@ -189,23 +186,10 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
-
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -216,14 +200,14 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, debug, p, device, rate, blocksize, nchans, format, info, stream, lock, control, trigger, devinfo, block, offset, autoscale
+    global patch, name, path, monitor
+    global debug, p, device, rate, blocksize, nchans, format, info, stream, lock, control, trigger, devinfo, block, offset, autoscale
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     p = pyaudio.PyAudio()
 
@@ -278,8 +262,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, debug, p, device, rate, blocksize, nchans, format, info, stream, lock, control, trigger, devinfo, block, offset, autoscale
+    global patch, name, path, monitor
+    global debug, p, device, rate, blocksize, nchans, format, info, stream, lock, control, trigger, devinfo, block, offset, autoscale
     global BUFFER, t, last, vco_pitch, vco_sin, vco_tri, vco_saw, vco_sqr, lfo_depth, lfo_frequency, adsr_attack, adsr_decay, adsr_sustain, adsr_release, vca_envelope, frequency, period, wave_sin, wave_tri, wave_saw, wave_sqr, waveform, lfo_envelope, adsr_envelope
 
     ################################################################################
@@ -356,7 +340,7 @@ def _loop_once():
 def _loop_forever():
     '''Run the main loop forever
     '''
-    global monitor, patch
+    global monitor
     while True:
         monitor.loop()
         _loop_once()
@@ -369,7 +353,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     control.stop()
     trigger.stop()
-    r.publish('SYNTHESIZER_UNBLOCK', 1)
+    patch.publish('SYNTHESIZER_UNBLOCK', 1)
     control.join()
     trigger.join()
     stream.stop_stream()
diff --git a/module/threshold/threshold.py b/module/threshold/threshold.py
index 7e13b8dc..a1ce638f 100755
--- a/module/threshold/threshold.py
+++ b/module/threshold/threshold.py
@@ -19,11 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import numpy as np
 import os
-import redis
 import sys
 import time
 
@@ -54,29 +51,25 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # there should not be any local variables in this function, they should all be global
+    if len(locals()):
+        print('LOCALS: ' + ', '.join(locals().keys()))
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general','debug'))
 
-    # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+def _start():
+    '''Start the module
+    This uses the global variables from setup and adds a set of global variables
+    '''
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, rectify, invert, prefix, window, scale_threshold, offset_threshold, scale_interval, offset_interval, channels, previous, begsample, endsample
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -88,18 +81,6 @@ def _setup():
     except:
         raise RuntimeError("cannot connect to FieldTrip buffer")
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
-
-def _start():
-    '''Start the module
-    This uses the global variables from setup and adds a set of global variables
-    '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input, name
-    global timeout, hdr_input, start, rectify, invert, prefix, window, scale_threshold, offset_threshold, scale_interval, offset_interval, channels, previous, begsample, endsample
-
     # this is the timeout for the FieldTrip buffer
     timeout = patch.getfloat('fieldtrip', 'timeout', default=30)
 
@@ -150,8 +131,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, monitor, debug, ft_host, ft_port, ft_input
-    global timeout, hdr_input, start, rectify, invert, prefix, window, scale_threshold, offset_threshold, scale_interval, offset_interval, channels, previous, begsample, endsample
+    global patch, name, path, monitor
+    global ft_host, ft_port, ft_input, timeout, hdr_input, start, rectify, invert, prefix, window, scale_threshold, offset_threshold, scale_interval, offset_interval, channels, previous, begsample, endsample
     global dat_input, threshold, interval, channel, maxind, maxval, sample, key
 
     # determine when we start polling for available data
@@ -214,7 +195,7 @@ def _loop_forever():
 def _stop():
     '''Stop and clean up on SystemExit, KeyboardInterrupt
     '''
-    global ft_input, monitor
+    global monitor, ft_input
     ft_input.disconnect()
     monitor.success('Disconnected from input FieldTrip buffer')
     sys.exit()
diff --git a/module/unicorn2ft/unicorn2ft.py b/module/unicorn2ft/unicorn2ft.py
index 831f29a2..fc763b02 100755
--- a/module/unicorn2ft/unicorn2ft.py
+++ b/module/unicorn2ft/unicorn2ft.py
@@ -19,10 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import os
-import redis
 import sys
 import time
 import serial
@@ -58,23 +55,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -85,15 +72,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, prefix, ft_host, ft_port, ft_output, timeout, s, start_acq, stop_acq, start_sequence, stop_sequence, blocksize, nchan, fsample
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
-    # get the options from the configuration file
-    timeout = patch.getfloat('unicorn', 'timeout', default=5)
-    blocksize = patch.getfloat('unicorn', 'blocksize', default=0.2) # write blocks of 0.2 seconds, i.e., 50 samples
+    global patch, name, path, monitor
+    global monitor, prefix, ft_host, ft_port, ft_output, timeout, blocksize, nchan, fsample, serialdevice, start_acq, stop_acq, start_sequence, stop_sequence, s, response
 
     try:
         ft_host = patch.getstring('fieldtrip', 'hostname')
@@ -104,7 +84,11 @@ def _start():
         monitor.info("Connected to output FieldTrip buffer")
     except:
         raise RuntimeError("cannot connect to output FieldTrip buffer")
-        
+
+    # get the options from the configuration file
+    timeout = patch.getfloat('unicorn', 'timeout', default=5)
+    blocksize = patch.getfloat('unicorn', 'blocksize', default=0.2) # write blocks of 0.2 seconds, i.e., 50 samples
+
     # write the header information to the FieldTrip buffer
     nchan = 16
     fsample = 250
@@ -145,8 +129,8 @@ def _start():
 def _loop_once():
     '''Run the main loop once
     '''
-    global parser, args, config, r, response, patch
-    global monitor, s, start_sequence, blocksize, nchan, fsample
+    global patch, name, path, monitor
+    global monitor, prefix, ft_host, ft_port, ft_output, timeout, blocksize, nchan, fsample, serialdevice, start_acq, stop_acq, start_sequence, stop_sequence, s, response
     
     nsample = int(blocksize*fsample)
     dat = np.zeros((nsample,nchan))
@@ -201,7 +185,7 @@ def _loop_once():
 def _loop_forever():
     '''Run the main loop forever
     '''
-    global monitor, patch, s
+    global monitor, patch
     while True:
         monitor.loop()
         _loop_once()
diff --git a/module/volcabass/volcabass.py b/module/volcabass/volcabass.py
index d0daf12e..b5399a59 100755
--- a/module/volcabass/volcabass.py
+++ b/module/volcabass/volcabass.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import threading
 import time
@@ -62,7 +59,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('VOLCABASS_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)    # this message contains the note
         while self.running:
@@ -85,23 +82,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -112,11 +99,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
 
     # the list of MIDI commands is the only aspect that is specific to the Volca Bass
     # see http://media.aadl.org/files/catalog_guides/1444141_chart.pdf
@@ -129,7 +113,7 @@ def _start():
                  82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143]
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # this is only for debugging, check which MIDI devices are accessible
     monitor.info('------ OUTPUT ------')
@@ -158,9 +142,9 @@ def _start():
     # each of the notes that can be played is mapped onto a different trigger
     trigger = []
     for name, code in zip(note_name, note_code):
-        if config.has_option('note', name):
+        if patch.config.has_option('note', name):
             # start the background thread that deals with this note
-            this = TriggerThread(patch.getstring('note', name), code)
+            this = TriggerThread(patch.get('note', name), code)
             trigger.append(this)
             monitor.debug(name + ' trigger configured')
 
@@ -182,8 +166,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
+    global patch, name, path, monitor
+    global control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
     global cmd, val, msg
 
     for name, cmd in zip(control_name, control_code):
@@ -203,10 +187,6 @@ def _loop_once():
         with lock:
             outputport.send(msg)
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
 
 def _loop_forever():
     '''Run the main loop forever
@@ -225,7 +205,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('VOLCABASS_UNBLOCK', 1)
+    patch.publish('VOLCABASS_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/volcabeats/volcabeats.py b/module/volcabeats/volcabeats.py
index 47d9aa66..37711627 100755
--- a/module/volcabeats/volcabeats.py
+++ b/module/volcabeats/volcabeats.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import threading
 import time
@@ -62,7 +59,7 @@ def stop(self):
         self.running = False
 
     def run(self):
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('VOLCABEATS_UNBLOCK') # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)    # this message contains the note
         while self.running:
@@ -85,23 +82,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -112,11 +99,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
 
     # the list of MIDI commands is the only aspect that is specific to the Volca Beats
     # see http://media.aadl.org/files/catalog_guides/1445131_chart.pdf
@@ -127,7 +111,7 @@ def _start():
     note_code = [36, 38, 43, 50, 42, 46, 39]
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # this is only for debugging, check which MIDI devices are accessible
     monitor.info('------ OUTPUT ------')
@@ -156,9 +140,9 @@ def _start():
     # each of the notes that can be played is mapped onto a different trigger
     trigger = []
     for name, code in zip(note_name, note_code):
-        if config.has_option('note', name):
+        if patch.config.has_option('note', name):
             # start the background thread that deals with this note
-            this = TriggerThread(patch.getstring('note', name), code)
+            this = TriggerThread(patch.get('note', name), code)
             trigger.append(this)
             monitor.debug(name + ' trigger configured')
 
@@ -180,8 +164,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
+    global patch, name, path, monitor
+    global control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
     global cmd, val, msg
 
     for name, cmd in zip(control_name, control_code):
@@ -201,10 +185,6 @@ def _loop_once():
         with lock:
             outputport.send(msg)
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
 
 def _loop_forever():
     '''Run the main loop forever
@@ -223,7 +203,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('VOLCABEATS_UNBLOCK', 1)
+    patch.publish('VOLCABEATS_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/volcakeys/volcakeys.py b/module/volcakeys/volcakeys.py
index d002bace..8e2d207b 100755
--- a/module/volcakeys/volcakeys.py
+++ b/module/volcakeys/volcakeys.py
@@ -19,12 +19,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import configparser
-import argparse
 import mido
 from fuzzywuzzy import process
 import os
-import redis
 import sys
 import threading
 import time
@@ -63,7 +60,7 @@ def stop(self):
 
     def run(self):
         global monitor, scale, offset
-        pubsub = r.pubsub()
+        pubsub = patch.pubsub()
         pubsub.subscribe('VOLCAKEYS_UNBLOCK')  # this message unblocks the redis listen command
         pubsub.subscribe(self.redischannel)    # this message contains the note
         while self.running:
@@ -86,23 +83,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -113,11 +100,8 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
-    global monitor, control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
-
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
+    global patch, name, path, monitor
+    global control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
 
     # the list of MIDI commands is the only aspect that is specific to the Volca Keys
     # see http://media.aadl.org/files/catalog_guides/1444140_chart.pdf
@@ -130,7 +114,7 @@ def _start():
                  82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143]
 
     # get the options from the configuration file
-    debug = patch.getint('general', 'debug')
+    debug = patch.getint('general', 'debug', default=1)
 
     # this is only for debugging, check which MIDI devices are accessible
     monitor.info('------ OUTPUT ------')
@@ -159,9 +143,9 @@ def _start():
     # each of the notes that can be played is mapped onto a different trigger
     trigger = []
     for name, code in zip(note_name, note_code):
-        if config.has_option('note', name):
+        if patch.config.has_option('note', name):
             # start the background thread that deals with this note
-            this = TriggerThread(patch.getstring('note', name), code)
+            this = TriggerThread(patch.get('note', name), code)
             trigger.append(this)
             monitor.debug(name + ' trigger configured')
 
@@ -183,8 +167,8 @@ def _loop_once():
     '''Run the main loop once
     This uses the global variables from setup and start, and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
-    global monitor, control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
+    global patch, name, path, monitor
+    global control_name, control_code, note_name, note_code, debug, port, midichannel, mididevice, outputport, scale, offset, lock, trigger, code, this, thread, previous_val
     global cmd, val, msg
 
     for name, cmd in zip(control_name, control_code):
@@ -204,10 +188,6 @@ def _loop_once():
         with lock:
             outputport.send(msg)
 
-    # there should not be any local variables in this function, they should all be global
-    if len(locals()):
-        print('LOCALS: ' + ', '.join(locals().keys()))
-
 
 def _loop_forever():
     '''Run the main loop forever
@@ -226,7 +206,7 @@ def _stop(*args):
     monitor.success('Closing threads')
     for thread in trigger:
         thread.stop()
-    r.publish('VOLCAKEYS_UNBLOCK', 1)
+    patch.publish('VOLCAKEYS_UNBLOCK', 1)
     for thread in trigger:
         thread.join()
     sys.exit()
diff --git a/module/vumeter/vumeter.py b/module/vumeter/vumeter.py
index bda0a8b6..2e606ac4 100755
--- a/module/vumeter/vumeter.py
+++ b/module/vumeter/vumeter.py
@@ -21,9 +21,6 @@
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtWidgets import QApplication, QWidget
-import configparser
-import redis
-import argparse
 import os
 import sys
 import time
@@ -125,23 +122,13 @@ def _setup():
     '''Initialize the module
     This adds a set of global variables
     '''
-    global parser, args, config, r, response, patch
+    global patch, name, path, monitor
 
-    parser = argparse.ArgumentParser()
-    parser.add_argument("-i", "--inifile", default=os.path.join(path, name + '.ini'), help="name of the configuration file")
-    args = parser.parse_args()
+    # configure and start the patch, this will parse the command-line arguments and the ini file
+    patch = EEGsynth.patch(name=name, path=path)
 
-    config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
-    config.read(args.inifile)
-
-    try:
-        r = redis.StrictRedis(host=config.get('redis', 'hostname'), port=config.getint('redis', 'port'), db=0, charset='utf-8', decode_responses=True)
-        response = r.client_list()
-    except redis.ConnectionError:
-        raise RuntimeError("cannot connect to Redis server")
-
-    # combine the patching from the configuration file and Redis
-    patch = EEGsynth.patch(config, r)
+    # this shows the splash screen and can be used to track parameters that have changed
+    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug', default=1))
 
     # there should not be any local variables in this function, they should all be global
     if len(locals()):
@@ -152,12 +139,9 @@ def _start():
     '''Start the module
     This uses the global variables from setup and adds a set of global variables
     '''
-    global parser, args, config, r, response, patch, name
+    global patch, name, path, monitor
     global monitor, delay, winx, winy, winwidth, winheight, input_name, input_variable, variable, app, window, timer
 
-    # this can be used to show parameters that have changed
-    monitor = EEGsynth.monitor(name=name, debug=patch.getint('general', 'debug'))
-
     # get the options from the configuration file
     delay           = patch.getfloat('general', 'delay')
     winx            = patch.getfloat('display', 'xpos')
@@ -166,7 +150,7 @@ def _start():
     winheight       = patch.getfloat('display', 'height')
 
     # get the input options
-    input_name, input_variable = list(zip(*config.items('input')))
+    input_name, input_variable = list(zip(*patch.config.items('input')))
 
     for name,variable in zip(input_name, input_variable):
         monitor.info("%s = %s" % (name, variable))
diff --git a/patches/copenhagen/historycontrol.ini b/patches/copenhagen/historycontrol.ini
index 9ffb3a2f..ff858408 100644
--- a/patches/copenhagen/historycontrol.ini
+++ b/patches/copenhagen/historycontrol.ini
@@ -11,7 +11,10 @@ stepsize=0.05
 enable=1
 
 [input]
-channels=spectral.channel1.delta,spectral.channel1.theta,spectral.channel1.alpha,spectral.channel1.beta,spectral.channel1.gamma,spectral.channel2.delta,spectral.channel2.theta,spectral.channel2.alpha,spectral.channel2.beta,spectral.channel2.gamma
+# channels=spectral.channel1.delta,spectral.channel1.theta,spectral.channel1.alpha,spectral.channel1.beta,spectral.channel1.gamma
+# channels=spectral.channel2.delta,spectral.channel2.theta,spectral.channel2.alpha,spectral.channel2.beta,spectral.channel2.gamma
+
+channels=spectral.channel1.alpha,spectral.channel2.alpha
 
 [output]
 ; the output name is constructed as channelXX.statistic, where statistic is any of


Test patch:
diff --git a/lib/Redis_test_client.py b/lib/Redis_test_client.py
new file mode 100644
index 00000000..27ef5949
--- /dev/null
+++ b/lib/Redis_test_client.py
@@ -0,0 +1,54 @@
+import threading
+import time
+
+broker='zeromq'
+
+if broker=='redis':
+    import redis
+    r = redis.StrictRedis(host='localhost', db=0, charset='utf-8', decode_responses=True)
+    r.client_list()
+elif broker=='dummy':
+    import DummyRedis
+    r = DummyRedis.client()
+elif broker=='fake':
+    import FakeRedis
+    r = FakeRedis.client()
+elif broker=='zeromq':
+    import ZmqRedis
+    r = ZmqRedis.client()
+
+r.set('a', 1)
+r.set('b', 2)
+# r.set('c', 3)
+
+print(r.get('a'))  # 1
+print(r.get('b'))  # 2
+print(r.get('c'))  # None
+
+print(r.exists('a'))  # True
+print(r.exists('b'))  # True
+print(r.exists('c'))  # False
+
+class WriteThread(threading.Thread):
+    def __init__(self, r):
+        threading.Thread.__init__(self)
+        self.r = r
+        self.count = 0
+    def run(self):
+        while True:
+            time.sleep(1)
+            self.count = self.count + 1
+            self.r.publish('a', self.count)
+
+background = WriteThread(r)
+background.start()
+
+pubsub = r.pubsub()
+pubsub.subscribe('a')
+pubsub.subscribe('b')
+pubsub.subscribe('c')
+print('-------------')
+
+while True:
+    for item in pubsub.listen():
+        print(item)