diff --git a/stmhal/modpyb.c b/stmhal/modpyb.c
index 96af7dcfc7e923d2434d3e35f7c525519a2bb4a6..b7df53f674904cc3b964316d844b9f128f953d4e 100644
--- a/stmhal/modpyb.c
+++ b/stmhal/modpyb.c
@@ -124,28 +124,6 @@ STATIC mp_obj_t pyb_elapsed_micros(mp_obj_t start) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_micros_obj, pyb_elapsed_micros);
 
-/// \function repl_uart(uart)
-/// Get or set the UART object that the REPL is repeated on.
-STATIC mp_obj_t pyb_repl_uart(mp_uint_t n_args, const mp_obj_t *args) {
-    if (n_args == 0) {
-        if (MP_STATE_PORT(pyb_stdio_uart) == NULL) {
-            return mp_const_none;
-        } else {
-            return MP_STATE_PORT(pyb_stdio_uart);
-        }
-    } else {
-        if (args[0] == mp_const_none) {
-            MP_STATE_PORT(pyb_stdio_uart) = NULL;
-        } else if (mp_obj_get_type(args[0]) == &pyb_uart_type) {
-            MP_STATE_PORT(pyb_stdio_uart) = args[0];
-        } else {
-            nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "need a UART object"));
-        }
-        return mp_const_none;
-    }
-}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_repl_uart_obj, 0, 1, pyb_repl_uart);
-
 MP_DECLARE_CONST_FUN_OBJ(pyb_main_obj); // defined in main.c
 
 STATIC const mp_map_elem_t pyb_module_globals_table[] = {
@@ -165,7 +143,7 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_stop), (mp_obj_t)&machine_sleep_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_standby), (mp_obj_t)&machine_deepsleep_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_main), (mp_obj_t)&pyb_main_obj },
-    { MP_OBJ_NEW_QSTR(MP_QSTR_repl_uart), (mp_obj_t)&pyb_repl_uart_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_repl_uart), (mp_obj_t)&mod_os_dupterm_obj },
 
     { MP_OBJ_NEW_QSTR(MP_QSTR_usb_mode), (mp_obj_t)&pyb_usb_mode_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_hid_mouse), (mp_obj_t)&pyb_usb_hid_mouse_obj },
diff --git a/stmhal/moduos.c b/stmhal/moduos.c
index 4a8261e41fbd066ff48d991bb862baed610a0119..8864ec582a73f0ae584963bbc8af025c25ba4c7f 100644
--- a/stmhal/moduos.c
+++ b/stmhal/moduos.c
@@ -35,6 +35,7 @@
 #include "lib/fatfs/diskio.h"
 #include "timeutils.h"
 #include "rng.h"
+#include "uart.h"
 #include "file.h"
 #include "sdcard.h"
 #include "fsusermount.h"
@@ -374,6 +375,28 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) {
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom);
 #endif
 
+// Get or set the UART object that the REPL is repeated on.
+// TODO should accept any object with read/write methods.
+STATIC mp_obj_t os_dupterm(mp_uint_t n_args, const mp_obj_t *args) {
+    if (n_args == 0) {
+        if (MP_STATE_PORT(pyb_stdio_uart) == NULL) {
+            return mp_const_none;
+        } else {
+            return MP_STATE_PORT(pyb_stdio_uart);
+        }
+    } else {
+        if (args[0] == mp_const_none) {
+            MP_STATE_PORT(pyb_stdio_uart) = NULL;
+        } else if (mp_obj_get_type(args[0]) == &pyb_uart_type) {
+            MP_STATE_PORT(pyb_stdio_uart) = args[0];
+        } else {
+            nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "need a UART object"));
+        }
+        return mp_const_none;
+    }
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_dupterm_obj, 0, 1, os_dupterm);
+
 STATIC const mp_map_elem_t os_module_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uos) },
 
@@ -397,6 +420,9 @@ STATIC const mp_map_elem_t os_module_globals_table[] = {
 #if MICROPY_HW_ENABLE_RNG
     { MP_OBJ_NEW_QSTR(MP_QSTR_urandom), (mp_obj_t)&os_urandom_obj },
 #endif
+
+    // these are MicroPython extensions
+    { MP_OBJ_NEW_QSTR(MP_QSTR_dupterm), (mp_obj_t)&mod_os_dupterm_obj },
 };
 
 STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table);
diff --git a/stmhal/portmodules.h b/stmhal/portmodules.h
index 821e7c5b668fa8db340a72a7eb432963a6c49ff9..68ae2f47cef3d854cef3548488d433abb9e1b13c 100644
--- a/stmhal/portmodules.h
+++ b/stmhal/portmodules.h
@@ -37,3 +37,4 @@ MP_DECLARE_CONST_FUN_OBJ(time_sleep_ms_obj);
 MP_DECLARE_CONST_FUN_OBJ(time_sleep_us_obj);
 
 MP_DECLARE_CONST_FUN_OBJ(mod_os_sync_obj);
+MP_DECLARE_CONST_FUN_OBJ(mod_os_dupterm_obj);
diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h
index 24b323828ed7ff487038f0c79dc4e5962b2c4297..94e9b6440a4769ae313df2fbc6a2a0a2e92683f2 100644
--- a/stmhal/qstrdefsport.h
+++ b/stmhal/qstrdefsport.h
@@ -400,6 +400,7 @@ Q(unlink)
 Q(sep)
 Q(stat)
 Q(urandom)
+Q(dupterm)
 
 // for time module
 Q(utime)