diff --git a/py/builtin.c b/py/builtin.c
index de1a8fc6a51f018924a63741c6d2d3eca2ab93be..b02608b40736323e32b16189fbc2f1a6b46483dc 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -207,6 +207,12 @@ STATIC mp_obj_t mp_builtin_hash(mp_obj_t o_in) {
 
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hash_obj, mp_builtin_hash);
 
+STATIC mp_obj_t mp_builtin_hex(mp_obj_t o_in) {
+    return mp_binary_op(MP_BINARY_OP_MODULO, MP_OBJ_NEW_QSTR(MP_QSTR__percent_x), o_in);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hex_obj, mp_builtin_hex);
+
 STATIC mp_obj_t mp_builtin_iter(mp_obj_t o_in) {
     return mp_getiter(o_in);
 }
diff --git a/py/builtin.h b/py/builtin.h
index caca71b5232da388dd9d9b229cb018eebf13fa41..51a74a310333879bc911dd0023d66f3b32c527e8 100644
--- a/py/builtin.h
+++ b/py/builtin.h
@@ -15,6 +15,7 @@ MP_DECLARE_CONST_FUN_OBJ(mp_builtin_exec_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_getattr_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_globals_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_hash_obj);
+MP_DECLARE_CONST_FUN_OBJ(mp_builtin_hex_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_id_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_isinstance_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_issubclass_obj);
diff --git a/py/builtintables.c b/py/builtintables.c
index cbf885844aebe956837f10936db262591e808792..6cf12dffa93d6c8325c39eb4ce205d2ec321cf26 100644
--- a/py/builtintables.c
+++ b/py/builtintables.c
@@ -60,6 +60,7 @@ STATIC const mp_map_elem_t mp_builtin_object_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_getattr), (mp_obj_t)&mp_builtin_getattr_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_globals), (mp_obj_t)&mp_builtin_globals_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_hash), (mp_obj_t)&mp_builtin_hash_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_hex), (mp_obj_t)&mp_builtin_hex_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_id), (mp_obj_t)&mp_builtin_id_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_isinstance), (mp_obj_t)&mp_builtin_isinstance_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_issubclass), (mp_obj_t)&mp_builtin_issubclass_obj },
diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py
index eaff11bd0c69ca125741abfdeff711942691bfc9..f8605ae093adff5d7a274aeea1e5c1280fe42387 100644
--- a/py/makeqstrdata.py
+++ b/py/makeqstrdata.py
@@ -16,6 +16,7 @@ codepoint2name[ord('-')] = 'hyphen';
 codepoint2name[ord('.')] = 'dot'
 codepoint2name[ord(':')] = 'colon'
 codepoint2name[ord('/')] = 'slash'
+codepoint2name[ord('%')] = 'percent'
 
 # this must match the equivalent function in qstr.c
 def compute_hash(qstr):
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index 80cf681ae780711fae18e5c9c8bbd28604237838..e9f2d8d9a419990ce160707b35a383b39d6649ae 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -106,6 +106,8 @@ Q(from_bytes)
 Q(getattr)
 Q(globals)
 Q(hash)
+Q(hex)
+Q(%x)
 Q(id)
 Q(io)
 Q(int)