Но почему каша в serial, и как его ответ сделать только bytes без исключения, требует размышления и осознания. Ведь переменная a - это bytes, и binascii.hexlify(a) это тоже bytes...
Потому что никакой каши нет. Ответ уже только bytes.
Просто когда выкидывашеь их в консоль, питон (точнее, метод __str__() класса bytes) пытается их представить "как можно читабельнее", т.е. пробует декодировать как ascii-строку что получится, а что не получится, представляет escape-последовательностями \x??.
Доказать легко:
b1 = bytes([1,2,3])
print(b1) # b'\x01\x02\x03'
# начиная с Python 3.8 можно указать разделитель в методе hex()
print(b1.hex(' ')) # 01 02 03
b2 = bytes([0x57, 0x54, 0x46])
print(b2) # b'WTF'
print(b2.hex(' ')) # 57 54 46
Как видим, работает идентично