diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c
index 2bd4da02291b342dc9d9adf54931a34cecae5b74..1ec54890f721f8763ec6cc9be57373bd0ec53d2e 100644
--- a/sys/fmt/fmt.c
+++ b/sys/fmt/fmt.c
@@ -53,6 +53,16 @@ static inline int _is_digit(char c)
     return (c >= '0' && c <= '9');
 }
 
+static inline int _is_upper(char c)
+{
+    return (c >= 'A' && c <= 'Z');
+}
+
+static inline char _to_lower(char c)
+{
+    return 'a' + (c - 'A');
+}
+
 size_t fmt_byte_hex(char *out, uint8_t byte)
 {
     if (out) {
@@ -405,6 +415,27 @@ size_t fmt_lpad(char *out, size_t in_len, size_t pad_len, char pad_char)
     return pad_len;
 }
 
+{
+size_t fmt_to_lower(char *out, const char *str)
+{
+    size_t len = 0;
+
+    while (str && *str) {
+        if (_is_upper(*str)) {
+            if (out) {
+                *out++ = _to_lower(*str);
+            }
+        }
+        else if (out) {
+            *out++ = *str;
+        }
+        str++;
+        len++;
+    }
+
+    return len;
+}
+
 uint32_t scn_u32_dec(const char *str, size_t n)
 {
     uint32_t res = 0;
diff --git a/sys/include/fmt.h b/sys/include/fmt.h
index 3e3d3c8fb278bd5051f30d2205454220b6869a84..8f927d5c3567af005e980b0657e5514c3b91d0b1 100644
--- a/sys/include/fmt.h
+++ b/sys/include/fmt.h
@@ -343,6 +343,14 @@ size_t fmt_strnlen(const char *str, size_t maxlen);
  */
 size_t fmt_str(char *out, const char *str);
 
+/**
+ * @brief   Copy null-terminated string to a lowercase string (excluding terminating \0)
+ *
+ * @param[out]  out     Pointer to output buffer, or NULL
+ * @param[in]   str     Pointer to null-terminated source string
+ */
+size_t fmt_to_lower(char *out, const char *str);
+
 /**
  * @brief Convert digits to uint32
  *
diff --git a/tests/unittests/tests-fmt/tests-fmt.c b/tests/unittests/tests-fmt/tests-fmt.c
index 0a7d537828c19432d58f74bee427413fbd907cd6..576444b9f8c05970614b9d9da40e8670a7808302 100644
--- a/tests/unittests/tests-fmt/tests-fmt.c
+++ b/tests/unittests/tests-fmt/tests-fmt.c
@@ -749,6 +749,16 @@ static void test_fmt_str(void)
     TEST_ASSERT_EQUAL_STRING(string1, &string2[0]);
 }
 
+static void test_fmt_to_lower(void)
+{
+    const char string_up[]  = "AbCdeFGHijkLM";
+    char string[]           = "zzzzzzzzzzzzzzz";
+
+    TEST_ASSERT_EQUAL_INT(fmt_strlen(string_up), fmt_to_lower(string, string_up));
+    string[fmt_strlen(string_up)] = '\0';
+    TEST_ASSERT_EQUAL_STRING("abcdefghijklm", &string[0]);
+}
+
 static void test_scn_u32_dec(void)
 {
     const char *string1 = "123456789";
@@ -817,6 +827,7 @@ Test *tests_fmt_tests(void)
         new_TestFixture(test_fmt_strlen),
         new_TestFixture(test_fmt_strnlen),
         new_TestFixture(test_fmt_str),
+        new_TestFixture(test_fmt_to_lower),
         new_TestFixture(test_scn_u32_dec),
         new_TestFixture(test_fmt_lpad),
     };