diff --git a/README.md b/README.md
index 780a9749fbff1e61c26d3288cf8ab7fea34e9ff7..5f9e698fc64513cfa3d87a8c392cbf315a61c900 100644
--- a/README.md
+++ b/README.md
@@ -26,10 +26,10 @@ The following Python code on the host computer lets you instantiate the device a
 
 ```python
 # test_led.py (Python)
-import mpy_bridge
+import mpybridge
 
 # instantiate the device on serial port COM1
-d = mpy_bridge.Device("COM1")
+d = mpybridge.Device("COM1")
 
 # prints available functions
 print(d)
@@ -63,10 +63,10 @@ A host program can send Python objects, and receive some in return. In this case
 
 ```python
 # test_pins.py (Python)
-import mpy_bridge
+import mpybridge
 
 # instantiate the device on serial port COM5
-d = mpy_bridge.Device("COM5")
+d = mpybridge.Device("COM5")
 
 # setup 3 pins at once
 d.setup_inputs([2, 6, 15])
@@ -74,7 +74,7 @@ d.setup_inputs([2, 6, 15])
 # return values of all requested pins
 values = d.read_inputs()
 
-print(values) # prints {2: 1, 6: 0, 15: 1}
+print(values)  # prints {2: 1, 6: 0, 15: 1}
 ```
 
 ## Prints handling
@@ -95,13 +95,13 @@ Exceptions on the MicroPython side are turned into Python exceptions, which can
 
 ```python
 # test_exceptions.py (Python)
-import mpy_bridge
+import mpybridge
 
-d = mpy_bridge.Device("COM5")
+d = mpybridge.Device("COM5")
 
 try:
     d.read_inputs()
-except mpy_bridge.MicroPythonError as e:
+except mpybridge.MicroPythonError as e:
     print(f"Error on the device: {e}")
 ```
 
@@ -111,10 +111,10 @@ This module includes a basic tool to update the main file on the MicroPython dev
 
 ```python
 # test_upload.py (Python)
-import mpy_bridge
+import mpybridge
 
 # instantiate device, skip init in case main file is missing
-d = mpy_bridge.Device("COM34", init=False)
+d = mpybridge.Device("COM34", init=False)
 
 # upload main file
 d.upload("embedded/main.py", "main.py")
diff --git a/examples/test_blink.py b/examples/test_blink.py
index aa81277ab6e973dd7cf5335985a022da04f57415..5c536a8647806fea5a4a7e110efc3f6e0bd29c54 100644
--- a/examples/test_blink.py
+++ b/examples/test_blink.py
@@ -1,10 +1,10 @@
-import mpy_bridge
+import mpybridge
 import time
 
 
 def main():
     # open device, read functions
-    d = mpy_bridge.Device("COM39")
+    d = mpybridge.Device("COM39")
 
     # print out functions
     print(d)
diff --git a/examples/test_time.py b/examples/test_time.py
index 5329ad9925479ebd64d76726934f55bce3b08024..075d4f91917dd750381f8a969f7995b218a23e57 100644
--- a/examples/test_time.py
+++ b/examples/test_time.py
@@ -1,11 +1,11 @@
 import datetime
-import mpy_bridge
+import mpybridge
 import numpy as np
 import matplotlib.pyplot as plt
 
 
 def main():
-    d = mpy_bridge.Device("COM39")
+    d = mpybridge.Device("COM39")
     n_exp = 1024
     results = []
 
diff --git a/examples/test_upload.py b/examples/test_upload.py
index 8c3e3a2bf2664f0eceee67e03192976df7a574fe..eee62965d159093952ac35587075447635612256 100644
--- a/examples/test_upload.py
+++ b/examples/test_upload.py
@@ -1,9 +1,9 @@
-import mpy_bridge
+import mpybridge
 
 
 def main():
     # Open device, skip reading its functions (the main file might be absent)
-    d = mpy_bridge.Device("COM39", init=False)
+    d = mpybridge.Device("COM39", init=False)
     # Upload main file
     d.upload("micropython/main.py", "main.py")
 
diff --git a/mpy_bridge.py b/mpybridge.py
similarity index 66%
rename from mpy_bridge.py
rename to mpybridge.py
index 6fe86ec0819be44e060be1d7581e64c8b30e6284..1db90e2ad7986c74bba66407729fcedc7bf008b2 100644
--- a/mpy_bridge.py
+++ b/mpybridge.py
@@ -8,6 +8,7 @@
 # warranty is provided, and users accept all liability.
 import os.path
 import serial
+import serial.tools.list_ports
 import time
 import re
 import ast
@@ -17,61 +18,77 @@ class MicroPythonError(RuntimeError):
     pass
 
 
+class MicroPythonFileError(MicroPythonError):
+    pass
+
+
 class Device:
-    init_delay = 0.1
+    init_delay = 0.15
+    timeout_delay = 2.0
 
-    def __init__(self, port, main_filename="main.py"):
-        self.ser_obj = serial.Serial(port)
+    def __init__(self, port, main_filename="main.py", show_prints=True, init=True):
+        self.ser_obj = serial.Serial(port, timeout=self.timeout_delay)
         self.function_args = {}
         self.function_callable = {}
         self.main_filename = main_filename
         self.main_module = main_filename.replace(".py", "")
-        # initialize
-        self.init()
-        self.read_functions()
+        self.show_prints = show_prints
+        self.reset()
+        if init:
+            self.init()
 
-    def init(self):
+    def reset(self):
         s = self.ser_obj
         s.write(b'\x04')
         s.write(b'\r\n\x03\r\n')
         s.write(b'\x01')
         time.sleep(self.init_delay)
-        # clean slate
+
         s.flushInput()
+        # wipe out previous state
+        for name in self.function_args:
+            delattr(self, name)
+        self.function_args = {}
+        self.function_callable = {}
+
+    def init(self):
+        # read functions
+        self.read_functions()
         # make sure main file is imported
         self.run(f'import {self.main_module}')
 
-    def run(self, cmd, show=False, end="\n"):
+    def run(self, cmd, end="\n", ignore_error=False):
         s = self.ser_obj
         s.write((cmd+end).encode("utf-8"))
         s.write(b'\x04')  # ^d reset
 
-        # >OK<RETURN>\x04
-        txt_ret = s.read_until(b"\x04")[3:-1].decode("utf-8")
+        # OK<RETURN>\x04
+        txt_ret = s.read_until(b"\x04")[2:-1].decode("utf-8")
 
-        # <ERROR>\x04
-        txt_err = s.read_until(b"\x04")[:-1].decode("utf-8")
+        # <ERROR>\x04>
+        txt_err = s.read_until(b"\x04>")[:-2].decode("utf-8")
 
-        if len(txt_err) > 0:
+        if len(txt_err) > 0 and not ignore_error:
             raise MicroPythonError(txt_err)
 
-        if show:
-            print(f"RETURN: '{txt_ret.rstrip()}'")
-
         return txt_ret.rstrip()
 
     def run_func(self, func_name, *args, **kwargs):
         args_list = list(repr(x) for x in args)
         kwargs_list = list(f"{a}={repr(b)}" for a, b in kwargs.items())
-        cmd_txt = f"print(repr({self.main_module}.{func_name}({','.join(args_list+kwargs_list)})))"
-        ret_txt = self.run(cmd_txt)
-        return ast.literal_eval(ret_txt)
+        cmd_txt = f"_ret = {self.main_module}.{func_name}({','.join(args_list+kwargs_list)})"
+        ret_print = self.run(cmd_txt)
+        if self.show_prints and len(ret_print) > 0:
+            print(f"MPY_PRINT@{self.ser_obj.port}:{ret_print}")
+        ret_val = self.run(f"print(repr(_ret))")
+        return ast.literal_eval(ret_val)
 
     def read_functions(self):
+        # open file
         try:
             self.run(f'f=open("{self.main_filename}","rb")')
-        except MicroPythonError:
-            raise FileNotFoundError(f"Could not find {self.main_filename} on device!")
+        except MicroPythonError as e:
+            raise MicroPythonFileError(str(e))
 
         # read main txt file
         main_txt = ast.literal_eval(self.run('print(f.read())')).decode("utf-8")
@@ -86,6 +103,13 @@ class Device:
             self.function_callable[name] = func
             setattr(self, name, func)
 
+    def testREPL(self):
+        # reduce waiting time for this simple test
+        txt = self.run("print(6*7)")
+        if len(txt) == 0:
+            return False
+        return ast.literal_eval(txt) == 42
+
     def __str__(self):
         txt = f"MicroPython Device at {self.ser_obj.port}, available functions:\n"
         if len(self.function_args) == 0:
@@ -102,17 +126,12 @@ class Device:
         self.close()
 
     def update(self):
-        for name in self.function_args:
-            delattr(self, name)
-        self.function_args = {}
-        self.function_callable = {}
         self.init()
-        self.read_functions()
 
     def upload_main(self, filename):
         self.upload(filename, self.main_filename)
 
-    def upload(self, filename, destination=None, update=True):
+    def upload(self, filename, destination=None, init=True):
         if destination is None:
             _, destination = os.path.split(filename)
         with open(filename, "r") as f:
@@ -120,8 +139,9 @@ class Device:
         self.run(f'f = open("{destination}", "wb")')
         self.run(f'f.write({repr(file_txt)})')
         self.run(f'f.close()')
-        if update:
-            self.update()
+        self.reset()
+        if init:
+            self.init()
 
     def remove(self, filename):
         self.run('import os')