diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c
index a921f287c4f28f442887ad645f731a9b282c40a0..b2aadef8b91f7420b1528f63ccf11282865616ba 100644
--- a/sys/fmt/fmt.c
+++ b/sys/fmt/fmt.c
@@ -62,6 +62,18 @@ size_t fmt_byte_hex(char *out, uint8_t byte)
     return 2;
 }
 
+size_t fmt_bytes_hex(char *out, const uint8_t *ptr, size_t n)
+{
+    size_t len = n * 2;
+    if (out) {
+        while (n--) {
+            out += fmt_byte_hex(out, *ptr++);
+        }
+    }
+
+    return len;
+}
+
 size_t fmt_strlen(const char *str)
 {
     const char *tmp = str;
@@ -95,6 +107,39 @@ size_t fmt_bytes_hex_reverse(char *out, const uint8_t *ptr, size_t n)
     return (n<<1);
 }
 
+static uint8_t _byte_mod25(uint8_t x)
+{
+    for (unsigned divisor = 200; divisor >= 25; divisor >>= 1) {
+        if (x >= divisor) {
+            x -= divisor;
+        }
+    }
+
+    return x;
+}
+
+static uint8_t _hex_nib(uint8_t nib)
+{
+    return _byte_mod25((nib & 0x1f) + 9);
+}
+
+size_t fmt_hex_bytes(uint8_t *out, const char *hex)
+{
+    size_t len = fmt_strlen(hex);
+
+    if (len & 1) {
+        out = NULL;
+        return 0;
+    }
+
+    size_t final_len = len >> 1;
+    for (size_t i = 0, j = 0; j < final_len; i += 2, j++) {
+        out[j] = (_hex_nib(hex[i]) << 4) | _hex_nib(hex[i+1]);
+    }
+
+    return final_len;
+}
+
 size_t fmt_u32_hex(char *out, uint32_t val)
 {
     return fmt_bytes_hex_reverse(out, (uint8_t*) &val, 4);
diff --git a/sys/include/fmt.h b/sys/include/fmt.h
index e1d0dfb38c5c81920937373b5cb36694afa19572..ceb98a5cf227018d57fafb7b1ff72dd3691b261d 100644
--- a/sys/include/fmt.h
+++ b/sys/include/fmt.h
@@ -61,6 +61,21 @@ extern "C" {
  */
 size_t fmt_byte_hex(char *out, uint8_t byte);
 
+/**
+ * @brief Formats a sequence of bytes as hex bytes
+ *
+ * Will write 2*n bytes to @p out.
+ * If @p out is NULL, will only return the number of bytes that would have
+ * been written.
+ *
+ * @param[out] out  Pointer to output buffer, or NULL
+ * @param[in]  ptr  Pointer to bytes to convert
+ * @param[in]  n    Number of bytes to convert
+ *
+ * @return     2*n
+ */
+size_t fmt_bytes_hex(char *out, const uint8_t *ptr, size_t n);
+
 /**
  * @brief Formats a sequence of bytes as hex bytes, starting with the last byte
  *
@@ -76,6 +91,23 @@ size_t fmt_byte_hex(char *out, uint8_t byte);
  */
 size_t fmt_bytes_hex_reverse(char *out, const uint8_t *ptr, size_t n);
 
+/**
+ * @brief Converts a sequence of hex bytes to an array of bytes
+ *
+ * The sequence of hex characters must have an even length:
+ * 2 hex character => 1 byte. If the sequence of hex has an odd length, this
+ * function returns 0 and an empty @p out.
+ *
+ * The hex characters sequence must contain valid hexadecimal characters
+ * otherwise the result in @p out is undefined.
+ *
+ * @param[out] out  Pointer to converted bytes, or NULL
+ * @param[in]  hex  Pointer to input buffer
+ * @returns    strlen(hex) / 2 when length of @p hex was even
+ * @returns    0 otherwise
+ */
+size_t fmt_hex_bytes(uint8_t *out, const char *hex);
+
 /**
  * @brief Convert a uint32 value to hex string.
  *
diff --git a/tests/unittests/tests-fmt/tests-fmt.c b/tests/unittests/tests-fmt/tests-fmt.c
index b69261935ad5e28ff48d04e6040738e368d7487c..df7f6ef4a1ad3f76f39c4402d184f4f0da0120a6 100644
--- a/tests/unittests/tests-fmt/tests-fmt.c
+++ b/tests/unittests/tests-fmt/tests-fmt.c
@@ -34,6 +34,53 @@ static void test_fmt_byte_hex(void)
     TEST_ASSERT_EQUAL_STRING("FF", (char *) out);
 }
 
+static void test_fmt_bytes_hex(void)
+{
+    char out[15] = "--------------";
+    uint8_t val[7] = { 0xAA, 9, 8, 7, 6, 0xA8, 0xEF};
+    uint8_t bytes = 0;
+
+    bytes = fmt_bytes_hex(out, val, 0);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(0, bytes);
+    TEST_ASSERT_EQUAL_STRING("", (char *) out);
+
+    bytes = fmt_bytes_hex(out, val, 1);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(2, bytes);
+    TEST_ASSERT_EQUAL_STRING("AA", (char *) out);
+
+    bytes = fmt_bytes_hex(out, val, 2);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(4, bytes);
+    TEST_ASSERT_EQUAL_STRING("AA09", (char *) out);
+
+    bytes = fmt_bytes_hex(out, val, 3);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(6, bytes);
+    TEST_ASSERT_EQUAL_STRING("AA0908", (char *) out);
+
+    bytes = fmt_bytes_hex(out, val, 4);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(8, bytes);
+    TEST_ASSERT_EQUAL_STRING("AA090807", (char *) out);
+
+    bytes = fmt_bytes_hex(out, val, 5);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(10, bytes);
+    TEST_ASSERT_EQUAL_STRING("AA09080706", (char *) out);
+
+    bytes = fmt_bytes_hex(out, val, 6);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(12, bytes);
+    TEST_ASSERT_EQUAL_STRING("AA09080706A8", (char *) out);
+
+    bytes = fmt_bytes_hex(out, val, 7);
+    out[bytes] = '\0';
+    TEST_ASSERT_EQUAL_INT(14, bytes);
+    TEST_ASSERT_EQUAL_STRING("AA09080706A8EF", (char *) out);
+}
+
 static void test_fmt_bytes_hex_reverse(void)
 {
     char out[9] = "--------";
@@ -61,6 +108,53 @@ static void test_fmt_bytes_hex_reverse(void)
     TEST_ASSERT_EQUAL_STRING("06070809", (char *) out);
 }
 
+static void test_fmt_hex_bytes(void)
+{
+    uint8_t val = 0;
+    uint8_t bytes = fmt_hex_bytes(&val, "");
+    TEST_ASSERT_EQUAL_INT(0, bytes);
+    TEST_ASSERT_EQUAL_INT(0, val);
+
+    bytes = fmt_hex_bytes(&val, "A");
+    TEST_ASSERT_EQUAL_INT(0, val);
+    TEST_ASSERT_EQUAL_INT(0, bytes);
+
+    char hex2[3] = "00";
+    uint8_t val1[1] = { 0 };
+    bytes = fmt_hex_bytes(val1, hex2);
+    TEST_ASSERT_EQUAL_INT(1, bytes);
+    TEST_ASSERT_EQUAL_INT(0, val1[0]);
+
+    memcpy(hex2, "AB", 2);
+    hex2[2] = '\0';
+    val1[0] = 0;
+    bytes = fmt_hex_bytes(val1, hex2);
+    TEST_ASSERT_EQUAL_INT(1, bytes);
+    TEST_ASSERT_EQUAL_INT(0xAB, val1[0]);
+
+    memcpy(hex2, "CD", 2);
+    hex2[2] = '\0';
+    val1[0] = 0;
+    bytes = fmt_hex_bytes(val1, hex2);
+    TEST_ASSERT_EQUAL_INT(1, bytes);
+    TEST_ASSERT_EQUAL_INT(0xCD, val1[0]);
+
+    memcpy(hex2, "EF", 2);
+    hex2[2] = '\0';
+    val1[0] = 0;
+    bytes = fmt_hex_bytes(val1, hex2);
+    TEST_ASSERT_EQUAL_INT(1, bytes);
+    TEST_ASSERT_EQUAL_INT(0xEF, val1[0]);
+
+    char hex6[] = "0102aF";
+    uint8_t val3[3];
+    bytes = fmt_hex_bytes(val3, hex6);
+    TEST_ASSERT_EQUAL_INT(3, bytes);
+    TEST_ASSERT_EQUAL_INT(1, val3[0]);
+    TEST_ASSERT_EQUAL_INT(2, val3[1]);
+    TEST_ASSERT_EQUAL_INT(0xAF, val3[2]);
+}
+
 static void test_fmt_u32_hex(void)
 {
     char out[9] = "--------";
@@ -376,7 +470,9 @@ Test *tests_fmt_tests(void)
 {
     EMB_UNIT_TESTFIXTURES(fixtures) {
         new_TestFixture(test_fmt_byte_hex),
+        new_TestFixture(test_fmt_bytes_hex),
         new_TestFixture(test_fmt_bytes_hex_reverse),
+        new_TestFixture(test_fmt_hex_bytes),
         new_TestFixture(test_fmt_u32_hex),
         new_TestFixture(test_fmt_u64_hex),
         new_TestFixture(test_fmt_u32_dec),