diff --git a/tests/periph_uart/tests/README.md b/tests/periph_uart/tests/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6e5d655ff5babcbf4cb5cb5e13850b8f118ef834
--- /dev/null
+++ b/tests/periph_uart/tests/README.md
@@ -0,0 +1,27 @@
+How To Run
+==========
+Connect the DUT (device under test) UART6 to the BPT (bluepill tester).
+Information for BPT setup can be found [here](https://github.com/MrKevinWeiss/Testing).
+
+### Example Test With Nucleo-F401RE
+1. Flash the periph_uart firmware on the nucleo-f401 with `make BOARD=nucleo-f401 flash` in the periph_uart directory
+2. Connect the [BPT](https://github.com/MrKevinWeiss/Testing/blob/master/resources/bptpinout.jpg) to the cpu using a USB to Serial converter</br>
+(BPT IF_TX = PA9)</br>
+(BPT IF_RX = PA10)
+3. Connect the BPT to the DUT</br>
+(DUT RX/PA12 <=> BPT TX3/PB10)</br>
+(DUT TX/PA11 <=> BPT RX3/PB11)</br>
+4. `test.py` requires the path to `$RIOTBASE/dist/tests` to be added to `PYTHONPATH` environment variable. Either modify `PYTHONPATH` permanently in the `.bashrc` or modify it temporary before invocation:</br>
+`PYTHONPATH=$PYTHONPATH:$RIOTBASE/dist/tests python3 test.py`
+5. Run the `test.py` from the `periph_uart/tests` directory (with various options)</br>
+`python3 test.py` for python 3</br>
+`python3 test.py --log=DEBUG` to see all debug messages</br>
+`python3 test.py --dut_port="/dev/ttyACM0"` to specify the port</br>
+`python3 test.py --log=DEBUG --dut_port="/dev/ttyACM0" --dut_uart=6 --bpt_port="/dev/ttyACM1" > nucleo-f401_test.txt` for all the fix'ns</br>
+
+Flags
+==========
+--log=DEBUG -> allows for debug output</br>
+--dut_port -> the port name of the DUT</br>
+--dut_uart -> DUT UART number to test</br>
+--bpt_port -> the port name of the BPT
diff --git a/tests/periph_uart/tests/periph_uart_if.py b/tests/periph_uart/tests/periph_uart_if.py
new file mode 100644
index 0000000000000000000000000000000000000000..e484b00c6abfafad0a3f81df84110f9d5b2587dd
--- /dev/null
+++ b/tests/periph_uart/tests/periph_uart_if.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2018 Kevin Weiss <kevin.weiss@haw-hamburg.de>
+#
+# This file is subject to the terms and conditions of the GNU Lesser
+# General Public License v2.1. See the file LICENSE in the top level
+# directory for more details.
+"""@package PyToAPI
+This module handles parsing of information from RIOT periph_uart test.
+"""
+from if_lib.dut_shell import DutShell
+
+
+class PeriphUartIf(DutShell):
+    """Interface to the node with periph_uart firmware."""
+
+    def uart_init(self, dev, baud):
+        """Initialize DUT's UART."""
+        return self.send_cmd("init {} {}".format(dev, baud))
+
+    def uart_send_string(self, dev, test_string):
+        """Send data via DUT's UART."""
+        return self.send_cmd("send {} {}".format(dev, test_string))
diff --git a/tests/periph_uart/tests/test.py b/tests/periph_uart/tests/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..7e0b8c810a9fd44f2b3f4f68f3b68ab5faf3603d
--- /dev/null
+++ b/tests/periph_uart/tests/test.py
@@ -0,0 +1,269 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2018 Kevin Weiss <kevin.weiss@haw-hamburg.de>
+#
+# This file is subject to the terms and conditions of the GNU Lesser
+# General Public License v2.1. See the file LICENSE in the top level
+# directory for more details.
+
+"""@package PyToAPI
+A test that can be used for now but should be adapted to a framework.
+TODO: REMOVE and adapt to a proper testing platform
+"""
+import argparse
+import errno
+import logging
+import random
+import string
+
+from periph_uart_if import PeriphUartIf
+from if_lib import bpt_if
+
+
+def kwexpect(val1, val2, level="WARN"):
+    """A poor mans pexpect"""
+    res = level
+    try:
+        if isinstance(val1, str) and isinstance(val2, str):
+            if val1.lower() == val2.lower():
+                res = "PASS"
+        elif isinstance(val1, list):
+            if len(val1) == 0:
+                return [res, val1, val2]
+            err = False
+            if len(val1) != len(val2):
+                err = True
+            for test_index in range(0, len(val1)):
+                if val1[test_index] != val2[test_index]:
+                    err = True
+            if err is False:
+                res = "PASS"
+            else:
+                return [level, val1, val2]
+        elif (val1 == val2) or (val1 is val2):
+            res = "PASS"
+    except TypeError:
+        res = 'TypeError'
+    except Exception as exc:
+        res = 'Unknown Error'
+        logging.debug(exc)
+
+    return [res, val1, val2]
+
+
+class TestParam:
+    """A poor mans way to store test params because I didn't know about dict"""
+    test_num = None
+    action = None
+    expect_res = None
+
+
+class Test:
+    name = ""
+    desc = ""
+    notes = ""
+    result = ""
+    cmd_log = None
+    num = 0
+
+    def __init__(self, name, desc, cmd_list):
+        self.name = name
+        self.desc = desc
+        self.cmd_log = cmd_list
+        self.result = "PASS"
+
+    def run_test(self, action, ex_res=None, ex_data=None,
+                 res_lvl="WARN", data_lvl="WARN"):
+        tp = TestParam()
+        tp.test_num = self.num
+        tp.action = action
+        if 'data' not in action:
+            tp.action['data'] = []
+        tp.expect_res = list()
+        if ex_res is not None:
+            tp.expect_res.append(kwexpect(action['result'], ex_res, res_lvl))
+            if tp.expect_res[-1][0] == "FAIL":
+                self.result = tp.expect_res[-1][0]
+            if tp.expect_res[-1][0] == "WARN" and self.result != "FAIL":
+                self.result = tp.expect_res[-1][0]
+        if ex_data is not None:
+
+            tp.expect_res.append(kwexpect(action['data'], ex_data, data_lvl))
+            if tp.expect_res[-1][0] == "FAIL":
+                self.result = tp.expect_res[-1][0]
+            if tp.expect_res[-1][0] == "WARN" and self.result != "FAIL":
+                self.result = tp.expect_res[-1][0]
+        self.num += 1
+        self.cmd_log.append(tp)
+        return action['data']
+
+    def skip_test(self, skipped_fxn):
+        tp = TestParam()
+
+        tp.test_num = self.num
+        self.num += 1
+        tp.action = skipped_fxn
+        tp.expect_res = "SKIP"
+        self.cmd_log.append(tp)
+
+    def manual_test(self, action):
+        tp = TestParam()
+
+        tp.test_num = self.num
+        self.num += 1
+        tp.action = action
+        tp.expect_res = "PASS"
+        self.cmd_log.append(tp)
+
+
+def setup_test(bpt, t):
+    bpt.reset_mcu()
+    t.manual_test("Reset BPT")
+
+    bpt.set_uart_baud(115200)
+    bpt.execute_changes()
+    t.manual_test("BPT: setup default baudrate")
+
+
+def increment_data(data):
+    """Increment each character."""
+    return ''.join(chr(ord(n) + 1) for n in data)
+
+
+def create_random_data(data_len):
+    """Generate random data with specified length."""
+    return 't' + ''.join([random.choice(
+        string.digits) for n in range(data_len)])
+
+
+def echo_test(bpt, uart, dut_uart, support_reset=False):
+    cmd_log = list()
+    t = Test('echo test',
+             'Tests DUT receive/transmit functionality in loopback mode',
+             cmd_log)
+
+    setup_test(bpt, t)
+
+    t.run_test(uart.uart_init(dut_uart, 115200), "Success")
+    t.run_test(uart.uart_send_string(dut_uart, "t111"), "Success", ["t111"])
+    data = create_random_data(100)
+    t.run_test(uart.uart_send_string(dut_uart, data), "Success", [data])
+    t.run_test(uart.uart_init(dut_uart, 38400), "Success")
+    t.run_test(uart.uart_send_string(dut_uart, "t111"), "Timeout")
+
+    return t
+
+
+def echo_ext_test(bpt, uart, dut_uart, support_reset=False):
+    cmd_log = list()
+    t = Test('echo ext test',
+             'Tests DUT receive/transmit functionality in ext loopback mode',
+             cmd_log)
+
+    setup_test(bpt, t)
+
+    bpt.set_uart_mode(1)
+    bpt.execute_changes()
+    t.manual_test("BPT: set echo ext mode")
+
+    t.run_test(uart.uart_init(dut_uart, 115200), "Success")
+    t.run_test(uart.uart_send_string(dut_uart, "t111"), "Success", ["u222"])
+    data = create_random_data(100)
+    t.run_test(uart.uart_send_string(dut_uart, data), "Success", [increment_data(data)])
+
+    return t
+
+
+def register_read_test(bpt, uart, dut_uart, support_reset=False):
+    cmd_log = list()
+    t = Test('register read test',
+             'Tests DUT receive/transmit functionality via reading BPT registers',
+             cmd_log)
+
+    setup_test(bpt, t)
+
+    bpt.set_uart_mode(2)
+    bpt.execute_changes()
+    t.manual_test("BPT: set echo ext mode")
+
+    t.run_test(uart.uart_init(dut_uart, 115200), "Success")
+    t.run_test(uart.uart_send_string(
+        dut_uart, "\"rr 152 10\""), "Success", ["0,0x09080706050403020100"])
+    t.run_test(uart.uart_send_string(
+        dut_uart, "\"rr -1 10\""), "Success", [errno.EINVAL])
+
+    return t
+
+
+def print_full_result(test):
+    """Print full test results."""
+    print('==================================================================')
+    print('Name:\t\t' + test.name)
+    print('Desc:\t\t' + test.desc)
+    print('Result:\t\t' + test.result)
+    print('Notes:\t\t' + test.notes)
+    print('------------------------------------------------------------------')
+    for test_param in test.cmd_log:
+        print('Test Number:\t***%d***' % test_param.test_num)
+        if not isinstance(test_param.action, str):
+            print('Command:\t' + test_param.action['cmd'])
+            if 'msg' in test_param.action:
+                print('Message:\t' + test_param.action['msg'])
+            if test_param.action['data'] is not None:
+                print('Data:\t\t[%s]' % ', '.join(map(str,
+                                                      test_param.action['data'])))
+            print('Result:\t\t' + test_param.action['result'])
+        else:
+            print('Command:\t' + test_param.action)
+        if isinstance(test_param.expect_res, str):
+            print('Expect Result:\t%s' % (test_param.expect_res))
+        else:
+            for res in test_param.expect_res:
+                print('Expect Result:\t%s (%s/%s)' % (res[0], res[1], res[2]))
+        print('----------')
+
+
+def print_results(test_list):
+    """Print brief test results."""
+    print('')
+    print('==================================================================')
+    for test in test_list:
+        print('Name:\t\t' + test.name)
+        print('Result:\t\t' + test.result + '\n')
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--log", help='Set the log level (DEBUG, INFO, WARN)')
+    parser.add_argument("--bpt_port", help='Port for the bluepill tester')
+    parser.add_argument("--dut_port", help='Port for device under test')
+    parser.add_argument("--dut_uart",
+                        help='DUT UART number to test',
+                        type=int,
+                        default=1)
+    args = parser.parse_args()
+
+    if args.log is not None:
+        loglevel = args.log
+        numeric_level = getattr(logging, loglevel.upper(), None)
+        if not isinstance(numeric_level, int):
+            raise ValueError('Invalid log level: %s' % loglevel)
+        logging.basicConfig(level=loglevel)
+
+    bpt = bpt_if.BptIf(port=args.bpt_port)
+    uart = PeriphUartIf(port=args.dut_port)
+
+    print('Starting Test periph_uart')
+    test_list = list()
+    test_list.append(echo_test(bpt, uart, args.dut_uart))
+    print_full_result(test_list[-1])
+    test_list.append(echo_ext_test(bpt, uart, args.dut_uart))
+    print_full_result(test_list[-1])
+    test_list.append(register_read_test(bpt, uart, args.dut_uart))
+    print_full_result(test_list[-1])
+
+    print_results(test_list)
+
+
+if __name__ == "__main__":
+    main()