diff --git a/build_otaconf_gateway.sh b/build_otaconf_gateway.sh
index 8bbc886b30c9d158ba7aa0badaa955151ac24f04..f18a14d4d0c88ddb5d3d48c3e52afb9cf3632682 100755
--- a/build_otaconf_gateway.sh
+++ b/build_otaconf_gateway.sh
@@ -3,8 +3,11 @@
 name="../otaconf-gateway.tapp"
 
 app="src/otaconf/gateway-node/autoexec.be"
-module1="src/otaconf/otaconf.be"
-# module2="src/mesh.be"
+module1="src/otaconf/gateway-node/globals.be"
+module2="src/otaconf/gateway-node/main.be"
+module3="src/otaconf/gateway-node/init/init_commands.be"
+module4="src/otaconf/gateway-node/init/init_mesh.be"
+module5="src/otaconf/gateway-node/actions/action_register_cluster.be"
 
 build="build"
 temp="build/temp"
@@ -13,7 +16,10 @@ mkdir "$build"
 mkdir "$temp"
 cp "$app" "$temp"
 cp "$module1" "$temp"
-# cp "$module2" "$temp"
+cp "$module2" "$temp"
+cp "$module3" "$temp"
+cp "$module4" "$temp"
+cp "$module5" "$temp"
 
 rm -rf "$name"
 cd "$temp"
diff --git a/src/otaconf/gateway-node/actions/action_register_cluster.be b/src/otaconf/gateway-node/actions/action_register_cluster.be
new file mode 100644
index 0000000000000000000000000000000000000000..63029ba702471d15178cbb589e220a91fb0d41fc
--- /dev/null
+++ b/src/otaconf/gateway-node/actions/action_register_cluster.be
@@ -0,0 +1,3 @@
+def _action_register_cluster(closure)
+  print('yey registered')
+end
\ No newline at end of file
diff --git a/src/otaconf/gateway-node/autoexec.be b/src/otaconf/gateway-node/autoexec.be
index 5961a152db78453825e328cb7d17d0d2a5abc6d2..8df2e10009bfa052d0e70dff1d9e27f17d7a49c7 100644
--- a/src/otaconf/gateway-node/autoexec.be
+++ b/src/otaconf/gateway-node/autoexec.be
@@ -1,244 +1,13 @@
-import mqtt
-import json
-import otaconf
-import persist
-import mesh
+# load global variables
+load(tasmota.wd + 'globals.be')
 
-var otaconf_finished = false
+# load init
+load(tasmota.wd + 'init_commands.be')
+load(tasmota.wd + 'init_mesh.be')
+# load(tasmota.wd + 'init_nbiot.be')
 
-var _otaconf_timer_id = 'otaconf_timer'
-var _wifi_used = tasmota.cmd('Wifi')['Wifi'] == 'ON'
-var _mesh_scan_retries = 3
-var _configuration_uuid = nil
-var _configuration_files = nil
-var _configuration_commands = nil
-var _own_command = nil
+# load actions
+load(tasmota.wd + 'action_register_cluster.be')
 
-
-def _set_ota_conf_interval(cmd, idx, payload)
-  if otaconf.set_poll_interval(payload)
-    tasmota.resp_cmnd_done()
-  else
-    tasmota.resp_cmnd_failed()
-  end
-end
-
-def _set_ota_server_url(cmd, idx, payload)
-  otaconf.set_server_url(payload)
-  tasmota.resp_cmnd_done()
-end
-
-def _otaconf_timeout()
-  otaconf_finished = true
-
-  tasmota.log('OTA: configuration timed out', 1)
-end
-
-def _otaconf_failed()
-  otaconf_finished = true
-
-  tasmota.log('OTA: configuration failed', 1)
-end
-
-def _finish_otaconf(early_exit)
-  if !early_exit
-    persist.last_otaconf_poll = tasmota.rtc()['local']
-    persist.save()
-  end
-
-  otaconf_finished = true
-
-  if _own_command != nil
-    tasmota.cmd(_own_command)
-  end
-
-  tasmota.log('OTA: configuration finished', 2)
-end
-
-def _config_status_updated()
-  tasmota.remove_timer(_otaconf_timer_id)
-
-  _finish_otaconf(false)
-end
-
-def _update_config_status()
-  if _configuration_uuid != nil
-    otaconf.update_config_status(_configuration_uuid, _config_status_updated)
-    tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
-  else
-    _otaconf_failed()
-  end
-end
-
-def _execute_commands()
-  while _configuration_commands.size() > 0
-    var configuration_command = _configuration_commands[0]
-    var device_macs = configuration_command['macs']
-    var command = configuration_command['command']
-
-    for device_mac : device_macs      
-      if device_mac == otaconf.get_mac()
-        _own_command = command
-      else
-        tasmota.log('OTA: remotely executing command on device: ' + device_mac, 2)
-      
-        var payload = {'action': 'EXECUTE_COMMAND', 'command': command}
-        mesh.send(otaconf.format_mac(device_mac), json.dump(payload))
-      end
-    end
-
-    _configuration_commands.remove(0)
-  end
-
-  _update_config_status()
-end
-
-def _distribute_files()
-  tasmota.log('OTA: distributing files', 2)
-
-  _execute_commands()
-end
-
-def _devices_registered()
-  tasmota.remove_timer(_otaconf_timer_id)
-
-  _distribute_files()
-end
-
-def _check_devices_registered()
-  var devices = mesh.get_peers()
-  var unregistered_devices = otaconf.get_unregistered_devices(devices)
-
-  if unregistered_devices.size() > 0
-    otaconf.register_devices(unregistered_devices, _devices_registered)
-    tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
-  else
-    _distribute_files()
-  end
-end
-
-def _scan_mesh()
-  if _mesh_scan_retries > 0
-    otaconf.send_mesh_scan_request()
-    _mesh_scan_retries -= 1
-    tasmota.set_timer(1000, _scan_mesh)
-  else
-    _check_devices_registered()
-  end
-end
-
-def _process_config(response)
-  tasmota.remove_timer(_otaconf_timer_id)
-
-  if response != nil && response.contains('configuration')
-    _configuration_uuid = response['uuid']
-    _configuration_files = response['configuration']['files']
-    _configuration_commands = response['configuration']['commands']
-
-    if _configuration_files.size() > 0
-      tasmota.log('OTA: attempting to fetch files', 2)
-
-      for file : _configuration_files
-        tasmota.log('OTA: fetching file: ' + file['uuid'], 2)
-
-        if file.contains('url')
-          tasmota.urlfetch(file['url'], file['uuid'])
-        else
-          tasmota.urlfetch(otaconf.get_server_url() + '/clusters/' + otaconf.get_cluster_uuid() + '/files/' + file['uuid'])
-        end
-      end
-    end
-
-    if response['configuration'].contains('mesh_scan') && response['configuration']['mesh_scan']
-      _scan_mesh()
-    else
-      _distribute_files()
-    end
-  else
-    _finish_otaconf(false)
-  end
-end
-
-def _check_config_pending()
-  otaconf.request_pending_config(_process_config)
-  tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
-end
-
-def _cluster_registered(response)
-  tasmota.remove_timer(_otaconf_timer_id)
-
-  if response == nil
-    _otaconf_failed()
-  end
-
-  _check_config_pending()
-end
-
-def _check_cluster_registered()
-  if !otaconf.is_cluster_registered()
-    otaconf.register_custer(_cluster_registered)
-    tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
-  else
-    _check_config_pending()
-  end
-end
-
-def _check_otaconf()
-  var next_otaconf_time = int(persist.find('last_otaconf_poll')) + otaconf.get_poll_interval()
-  var current_time = tasmota.rtc()['local']
-
-  if !persist.has('last_otaconf_poll') || (current_time > next_otaconf_time) 
-    _check_cluster_registered()
-  else
-    var remaining_hours = str((next_otaconf_time - current_time) / 3600)
-    var remaining_min = str(((next_otaconf_time - current_time) % 3600) / 60)
-    tasmota.log('OTA: next configuration poll in ~ ' + remaining_hours + ' hours ' + remaining_min + ' minutes', 2)
-    _finish_otaconf(true)
-  end
-end
-
-def _init_nbiot()
-  print('init nb-iot') # todo implement
-end
-
-def _callback(sender, payload)
-  var recieved_data = json.load(payload)
-  print(recieved_data) # todo remove
-  print(mesh.get_peers()) # todo remove
-
-  if recieved_data != nil && recieved_data.contains('action')
-    var action = recieved_data['action']
-  end
-end
-
-def _mesh_init()
-  mesh.start()
-  mesh.register_callback(_callback)
-end
-
-def _mesh_pre_init()
-  mesh.init_soft_ap_mode(1)   
-  
-  tasmota.set_timer(50, _mesh_init)
-end
-
-def _wakeup_task()
-
-  if !_wifi_used
-    _init_nbiot()
-    _mesh_pre_init()
-  else 
-    _mesh_init()
-  end
-
-  tasmota.set_timer(1000, _check_otaconf)
-end
-
-if !_wifi_used
-  tasmota.add_rule('system#init', _wakeup_task)
-else
-  tasmota.add_rule('system#boot', _wakeup_task)
-end
-
-tasmota.add_cmd('OtaConfInterval', _set_ota_conf_interval)
-tasmota.add_cmd('OtaServerUrl', _set_ota_server_url)
+# load main
+load(tasmota.wd + 'main.be')
\ No newline at end of file
diff --git a/src/otaconf/gateway-node/autoexec_old.be b/src/otaconf/gateway-node/autoexec_old.be
new file mode 100644
index 0000000000000000000000000000000000000000..fea1c10a1d65e9f15e12e39b9a5a282876e2b012
--- /dev/null
+++ b/src/otaconf/gateway-node/autoexec_old.be
@@ -0,0 +1,254 @@
+import mqtt
+import json
+import otaconf
+import persist
+import mesh
+
+var otaconf_finished = false
+
+var _otaconf_timer_id = 'otaconf_timer'
+var _wifi_used = tasmota.cmd('Wifi')['Wifi'] == 'ON'
+var _mesh_scan_retries = 3
+var _file_send_retries = 3
+var _file_in_process = nil
+var _configuration_uuid = nil
+var _configuration_files = nil
+var _configuration_commands = nil
+var _own_command = nil
+
+def _otaconf_timeout()
+  otaconf_finished = true
+
+  tasmota.log('OTA: configuration timed out', 1)
+end
+
+def _otaconf_failed()
+  otaconf_finished = true
+
+  tasmota.log('OTA: configuration failed', 1)
+end
+
+def _finish_otaconf(early_exit)
+  if !early_exit
+    persist.last_otaconf_poll = tasmota.rtc()['local']
+    persist.save()
+  end
+
+  otaconf_finished = true
+
+  if _own_command != nil
+    tasmota.cmd(_own_command)
+  end
+
+  tasmota.log('OTA: configuration finished', 2)
+end
+
+def _config_status_updated()
+  tasmota.remove_timer(_otaconf_timer_id)
+
+  _finish_otaconf(false)
+end
+
+def _update_config_status()
+  if _configuration_uuid != nil
+    otaconf.update_config_status(_configuration_uuid, _config_status_updated)
+    tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
+  else
+    _otaconf_failed()
+  end
+end
+
+def _execute_commands()
+  while _configuration_files.size() > 0
+    var configuration_file = _configuration_files[0]
+    var device_macs = configuration_command['macs']
+    var command = configuration_command['command']
+
+    # todo move to otaconf
+    for device_mac : device_macs      
+      if device_mac == otaconf.get_mac()
+        _own_command = command
+      else
+        tasmota.log('OTA: remotely executing command on device: ' + device_mac, 2)
+      
+        var payload = {'action': 'EXECUTE_COMMAND', 'command': command}
+        mesh.send(otaconf.format_mac(device_mac), json.dump(payload))
+      end
+    end
+
+    _configuration_commands.remove(0)
+  end
+
+  _update_config_status()
+end
+
+def _send_batch
+
+def _distribute_files()
+  while _configuration_commands.size() > 0
+    var configuration_command = _configuration_commands[0]
+    var device_macs = configuration_command['macs']
+    var command = configuration_command['command']
+
+    # todo move to otaconf
+    for device_mac : device_macs      
+      if device_mac == otaconf.get_mac()
+        _own_command = command
+      else
+        tasmota.log('OTA: remotely executing command on device: ' + device_mac, 2)
+      
+        var payload = {'action': 'EXECUTE_COMMAND', 'command': command}
+        mesh.send(otaconf.format_mac(device_mac), json.dump(payload))
+      end
+    end
+
+    _configuration_commands.remove(0)
+  end
+
+  _execute_commands()
+end
+
+def _devices_registered()
+  tasmota.remove_timer(_otaconf_timer_id)
+
+  _distribute_files()
+end
+
+def _check_devices_registered()
+  var devices = mesh.get_peers()
+  var unregistered_devices = otaconf.get_unregistered_devices(devices)
+
+  if unregistered_devices.size() > 0
+    otaconf.register_devices(unregistered_devices, _devices_registered)
+    tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
+  else
+    _distribute_files()
+  end
+end
+
+def _scan_mesh()
+  if _mesh_scan_retries > 0
+    otaconf.send_mesh_scan_request()
+    _mesh_scan_retries -= 1
+    tasmota.set_timer(5000, _scan_mesh)
+  else
+    _check_devices_registered()
+  end
+end
+
+def _process_config(response)
+  tasmota.remove_timer(_otaconf_timer_id)
+
+  if response != nil && response.contains('configuration')
+    _configuration_uuid = response['uuid']
+    _configuration_files = response['configuration']['files']
+    _configuration_commands = response['configuration']['commands']
+
+    # todo move to otaconf
+    if _configuration_files.size() > 0
+      tasmota.log('OTA: attempting to fetch files', 2)
+
+      for file : _configuration_files
+        tasmota.log('OTA: fetching file: ' + file['uuid'], 2)
+
+        if file.contains('url')
+          tasmota.urlfetch(file['url'], file['uuid'])
+        else
+          tasmota.urlfetch(otaconf.get_server_url() + '/clusters/' + otaconf.get_cluster_uuid() + '/files/' + file['uuid'])
+        end
+      end
+    end
+
+    if response['configuration'].contains('mesh_scan') && response['configuration']['mesh_scan']
+      _scan_mesh()
+    else
+      _distribute_files()
+    end
+  else
+    _finish_otaconf(false)
+  end
+end
+
+def _check_config_pending()
+  otaconf.request_pending_config(_process_config)
+  tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
+end
+
+def _cluster_registered(response)
+  tasmota.remove_timer(_otaconf_timer_id)
+
+  if response == nil
+    _otaconf_failed()
+  end
+
+  _check_config_pending()
+end
+
+def _check_cluster_registered()
+  if !otaconf.is_cluster_registered()
+    otaconf.register_custer(_cluster_registered)
+    tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id)
+  else
+    _check_config_pending()
+  end
+end
+
+def _check_otaconf()
+  var next_otaconf_time = int(persist.find('last_otaconf_poll')) + otaconf.get_poll_interval()
+  var current_time = tasmota.rtc()['local']
+
+  if !persist.has('last_otaconf_poll') || (current_time > next_otaconf_time) 
+    _check_cluster_registered()
+  else
+    var remaining_hours = str((next_otaconf_time - current_time) / 3600)
+    var remaining_min = str(((next_otaconf_time - current_time) % 3600) / 60)
+    tasmota.log('OTA: next configuration poll in ~ ' + remaining_hours + ' hours ' + remaining_min + ' minutes', 2)
+    _finish_otaconf(true)
+  end
+end
+
+def _init_nbiot()
+  print('init nb-iot') # todo implement
+end
+
+def _callback(sender, payload)
+  var recieved_data = json.load(payload)
+  print(recieved_data) # todo remove
+  print(mesh.get_peers()) # todo remove
+
+  if recieved_data != nil && recieved_data.contains('action')
+    var action = recieved_data['action']
+  end
+end
+
+def _mesh_init()
+  mesh.start()
+  mesh.register_callback(_callback)
+end
+
+def _mesh_pre_init()
+  mesh.init_soft_ap_mode(1)   
+  
+  tasmota.set_timer(50, _mesh_init)
+end
+
+def _wakeup_task()
+
+  if !_wifi_used
+    _init_nbiot()
+    _mesh_pre_init()
+  else 
+    _mesh_init()
+  end
+
+  tasmota.set_timer(1000, _check_otaconf)
+end
+
+if !_wifi_used
+  tasmota.add_rule('system#init', _wakeup_task)
+else
+  tasmota.add_rule('system#boot', _wakeup_task)
+end
+
+tasmota.add_cmd('OtaConfInterval', _set_ota_conf_interval)
+tasmota.add_cmd('OtaServerUrl', _set_ota_server_url)
diff --git a/src/otaconf/gateway-node/globals.be b/src/otaconf/gateway-node/globals.be
new file mode 100644
index 0000000000000000000000000000000000000000..a3faadc4d3cdf60573794fc1eea0eb4eb2f0e59c
--- /dev/null
+++ b/src/otaconf/gateway-node/globals.be
@@ -0,0 +1,13 @@
+import persist
+
+# global variables exposed to the application developer
+var otaconf_finished = false
+
+# global variables for internal use
+var _g_wifi_used = tasmota.cmd('Wifi')['Wifi'] == 'ON'
+var _g_mesh_init = false
+var _g_nbiot_init = false
+var _g_request_topic = 'otaconf/%s/request'
+var _g_response_topic = 'otaconf/%s/response'
+var _g_server_url = persist.find('otaconf_server_url', '')
+var _g_poll_interval = int(persist.find('otaconf_poll_interval', 86400)) # default: 24h
\ No newline at end of file
diff --git a/src/otaconf/gateway-node/init/init_commands.be b/src/otaconf/gateway-node/init/init_commands.be
new file mode 100644
index 0000000000000000000000000000000000000000..e1a563fca6f0cf461bd28538e0471c83934a2951
--- /dev/null
+++ b/src/otaconf/gateway-node/init/init_commands.be
@@ -0,0 +1,24 @@
+import re
+
+def _set_ota_conf_interval(cmd, idx, payload)
+  if re.match('^[0-9]*$', payload) != nil
+    _g_poll_interval = payload
+
+    persist.otaconf_poll_interval = payload
+    persist.save()
+    
+    tasmota.resp_cmnd_done()
+  else
+    tasmota.resp_cmnd_failed()
+  end
+end
+  
+def _set_ota_server_url(cmd, idx, payload)
+  _g_server_url = payload
+  persist.otaconf_server_url = payload
+  persist.save()
+  tasmota.resp_cmnd_done()
+end
+
+tasmota.add_cmd('OtaConfInterval', _set_ota_conf_interval)
+tasmota.add_cmd('OtaServerUrl', _set_ota_server_url)
\ No newline at end of file
diff --git a/src/otaconf/gateway-node/init/init_mesh.be b/src/otaconf/gateway-node/init/init_mesh.be
new file mode 100644
index 0000000000000000000000000000000000000000..6e4182e0d62a998d7ca86f81e480c48e04e140b0
--- /dev/null
+++ b/src/otaconf/gateway-node/init/init_mesh.be
@@ -0,0 +1,25 @@
+import mesh
+
+def _mesh_start()
+  mesh.start()
+
+  _g_mesh_init = true
+end
+
+# init soft accesspoint mode
+def _init_soft_ap_mode()
+  mesh.init_soft_ap_mode(1)   
+
+  tasmota.set_timer(50, _mesh_start)
+end
+
+# init standard accesspoint stationary mode
+def _init_ap_sta_mode()
+  _mesh_start()
+end
+
+if !_g_wifi_used
+  tasmota.add_rule('system#init', _init_soft_ap_mode)
+else
+  tasmota.add_rule('system#boot', _init_ap_sta_mode)
+end
\ No newline at end of file
diff --git a/src/otaconf/gateway-node/main.be b/src/otaconf/gateway-node/main.be
new file mode 100644
index 0000000000000000000000000000000000000000..cb66182e2d2e36330fae070ff0aff846734f641d
--- /dev/null
+++ b/src/otaconf/gateway-node/main.be
@@ -0,0 +1,47 @@
+import persist
+
+def _finish_otaconf(early_exit)
+  if !early_exit
+    persist.otaconf_last_poll = tasmota.rtc()['local']
+    persist.save()
+  end
+
+  otaconf_finished = true
+
+  #if _own_command != nil
+  #  tasmota.cmd(_own_command)
+  #end
+
+  tasmota.log('OTA: configuration finished', 2)
+end
+
+def _check_otaconf()
+  var next_otaconf_time = int(persist.find('otaconf_last_poll')) + _g_poll_interval
+  var current_time = tasmota.rtc()['local']
+
+  if !persist.has('otaconf_last_poll') || (current_time > next_otaconf_time) 
+    _action_register_cluster()
+  else
+    var remaining_hours = str((next_otaconf_time - current_time) / 3600)
+    var remaining_min = str(((next_otaconf_time - current_time) % 3600) / 60)
+
+    tasmota.log('OTA: next configuration poll in ~ ' + remaining_hours + ' hours ' + remaining_min + ' minutes', 2)
+
+    _finish_otaconf(true)
+  end
+end
+
+# todo first check if otaconf necessary, then check mesh etc
+def _wake_up_task()
+  if !_g_mesh_init # || (!_g_wifi_used && !_nbiot_init)
+    tasmota.set_timer(1000, _wake_up_task)
+  end
+
+  tasmota.set_timer(1000, _check_otaconf)
+end
+
+if !_g_wifi_used
+  tasmota.add_rule('system#init', _wake_up_task)
+else
+  tasmota.add_rule('system#boot', _wake_up_task)
+end
\ No newline at end of file
diff --git a/src/otaconf/gateway-node/modules/nbiot b/src/otaconf/gateway-node/modules/nbiot
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/otaconf/otaconf.be b/src/otaconf/gateway-node/otaconf.be
similarity index 95%
rename from src/otaconf/otaconf.be
rename to src/otaconf/gateway-node/otaconf.be
index 0d52f14211b0ced87c095fa4f86c19aae31757c8..16146513200cc3e0f0c470c67f9408e0d6be1fec 100644
--- a/src/otaconf/otaconf.be
+++ b/src/otaconf/gateway-node/otaconf.be
@@ -4,6 +4,12 @@ import mqtt
 import mesh
 import re
 
+class FileSender
+  var _
+
+  def 
+end
+
 class ResponseHandler
   def handle_cluster_registered(response)
     if !response.contains('content') || !response['content'].contains('uuid')
@@ -129,18 +135,6 @@ otaconf.init = def (m)
       return self.poll_interval
     end
 
-    def set_poll_interval(interval)
-      if re.match('^[0-9]*$', interval) != nil
-        self.poll_interval = interval
-        persist.otaconf_poll_interval = interval
-        persist.save()
-        
-        return true
-      else
-        return false
-      end
-    end
-
     def get_server_url()
       return self.server_url
     end
diff --git a/src/otaconf/sensor-node/autoexec.be b/src/otaconf/sensor-node/autoexec.be
index d613f6e030763add51b1b25ea9f026783a7df3ed..5999aa63c2240d4f9a2c8a3026991b590056922f 100644
--- a/src/otaconf/sensor-node/autoexec.be
+++ b/src/otaconf/sensor-node/autoexec.be
@@ -97,14 +97,14 @@ end
 
 def _wakeup_task()
 
-  if !_wifi_used
+  if !_g_wifi_used
     _mesh_pre_init()
   else 
     _mesh_init()
   end
 end
 
-if !_wifi_used
+if !_g_wifi_used
   tasmota.add_rule('system#init', _wakeup_task)
 else
   tasmota.add_rule('system#boot', _wakeup_task)