diff --git a/extmod/modlwip.c b/extmod/modlwip.c
index 84a1bd3c17ed7a31dac315e01845cb211f39f18f..d76fd1b1e47ce38d0ba5203f63be7a1648a734ea 100644
--- a/extmod/modlwip.c
+++ b/extmod/modlwip.c
@@ -1454,7 +1454,7 @@ STATIC mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
             && (type == 0 || type == MOD_NETWORK_SOCK_STREAM)
             && proto == 0
             && flags == 0)) {
-            mp_warning("unsupported getaddrinfo constraints");
+            mp_warning(MP_WARN_CAT(RuntimeWarning), "unsupported getaddrinfo constraints");
         }
     }
 
diff --git a/ports/unix/mpconfigport_coverage.h b/ports/unix/mpconfigport_coverage.h
index 2519482b01a4227cdbcef8aae4def43a74e7b53a..87bf8454cec197382eaf83fbb0344cd2b216a01c 100644
--- a/ports/unix/mpconfigport_coverage.h
+++ b/ports/unix/mpconfigport_coverage.h
@@ -36,6 +36,7 @@
 #define MICROPY_FLOAT_HIGH_QUALITY_HASH (1)
 #define MICROPY_ENABLE_SCHEDULER       (1)
 #define MICROPY_READER_VFS             (1)
+#define MICROPY_WARNINGS_CATEGORY      (1)
 #define MICROPY_MODULE_GETATTR         (1)
 #define MICROPY_PY_DELATTR_SETATTR     (1)
 #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1)
diff --git a/ports/zephyr/modusocket.c b/ports/zephyr/modusocket.c
index 84d6fa72975c2e225a04db3225f9a5132d8780c3..083083788a9f15818e0003015f870c71bf402378 100644
--- a/ports/zephyr/modusocket.c
+++ b/ports/zephyr/modusocket.c
@@ -288,7 +288,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv);
 
 STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) {
     (void)n_args; // always 4
-    mp_warning("setsockopt() not implemented");
+    mp_warning(MP_WARN_CAT(RuntimeWarning), "setsockopt() not implemented");
     return mp_const_none;
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt);
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 48427c3e586101ccfe63245d6d129847fce0f8ab..4fd08db230c970cc2397a78fc852a6f779b087ca 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -598,6 +598,11 @@ typedef long long mp_longint_impl_t;
 #define MICROPY_WARNINGS (0)
 #endif
 
+// Whether to support warning categories
+#ifndef MICROPY_WARNINGS_CATEGORY
+#define MICROPY_WARNINGS_CATEGORY (0)
+#endif
+
 // This macro is used when printing runtime warnings and errors
 #ifndef MICROPY_ERROR_PRINTER
 #define MICROPY_ERROR_PRINTER (&mp_plat_print)
@@ -1508,4 +1513,15 @@ typedef double mp_float_t;
 #endif
 #endif
 
+// Warning categories are by default implemented as strings, though
+// hook is left for a port to define them as something else.
+#if MICROPY_WARNINGS_CATEGORY
+# ifndef MP_WARN_CAT
+# define MP_WARN_CAT(x) #x
+# endif
+#else
+# undef MP_WARN_CAT
+# define MP_WARN_CAT(x) (NULL)
+#endif
+
 #endif // MICROPY_INCLUDED_PY_MPCONFIG_H
diff --git a/py/obj.c b/py/obj.c
index 47b7d15ae0a83a59b1aa01d0d027d8d17ade1ceb..7007d5070e28297166dd8196771f005018c8727d 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -202,7 +202,7 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) {
     str_cmp_err:
         #if MICROPY_PY_STR_BYTES_CMP_WARN
         if (MP_OBJ_IS_TYPE(o1, &mp_type_bytes) || MP_OBJ_IS_TYPE(o2, &mp_type_bytes)) {
-            mp_warning("Comparison between bytes and str");
+            mp_warning(MP_WARN_CAT(BytesWarning), "Comparison between bytes and str");
         }
         #endif
         return false;
diff --git a/py/runtime.h b/py/runtime.h
index dd4c9a984eb904c556fd16cab5ffefedfe255b62..9811c1b5ae8c2a581907626cc3412338c3d2e1c7 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -179,7 +179,9 @@ void mp_native_raise(mp_obj_t o);
 #define mp_sys_argv (MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_sys_argv_obj)))
 
 #if MICROPY_WARNINGS
-void mp_warning(const char *msg, ...);
+#ifndef mp_warning
+void mp_warning(const char *category, const char *msg, ...);
+#endif
 #else
 #define mp_warning(...)
 #endif
diff --git a/py/vm.c b/py/vm.c
index f9f9a3d6acf19d453739062418bf34add978cda8..fce59349f05e337a6f47e2bd96ae3bb12d039452 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -1116,7 +1116,7 @@ unwind_return:
                     mp_uint_t unum = *ip;
                     mp_obj_t obj;
                     if (unum == 2) {
-                        mp_warning("exception chaining not supported");
+                        mp_warning(NULL, "exception chaining not supported");
                         // ignore (pop) "from" argument
                         sp--;
                     }
diff --git a/py/warning.c b/py/warning.c
index 12d0f9c99b03262b2f74f0db5938671e51036801..ebaf2d07832d0c272b4ce8fca7e7039273c2f4dd 100644
--- a/py/warning.c
+++ b/py/warning.c
@@ -32,10 +32,15 @@
 
 #if MICROPY_WARNINGS
 
-void mp_warning(const char *msg, ...) {
+void mp_warning(const char *category, const char *msg, ...) {
+    if (category == NULL) {
+        category = "Warning";
+    }
+    mp_print_str(MICROPY_ERROR_PRINTER, category);
+    mp_print_str(MICROPY_ERROR_PRINTER, ": ");
+
     va_list args;
     va_start(args, msg);
-    mp_print_str(MICROPY_ERROR_PRINTER, "Warning: ");
     mp_vprintf(MICROPY_ERROR_PRINTER, msg, args);
     mp_print_str(MICROPY_ERROR_PRINTER, "\n");
     va_end(args);
@@ -43,7 +48,7 @@ void mp_warning(const char *msg, ...) {
 
 void mp_emitter_warning(pass_kind_t pass, const char *msg) {
     if (pass == MP_PASS_CODE_SIZE) {
-        mp_warning(msg, NULL);
+        mp_warning(NULL, msg);
     }
 }