Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Mi Band 2 - Font pack / unpack
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Simple python command line tool to pack / unpack the Mi Band 2 font firmware files
from PIL import Image
import os
import math
import binascii
import sys
# Unpack the Mi Band 2 font file
# Creates 1bpp bmp and txt file with the characters
def unpackFont(font_path):
print('Unpacking', font_path)
font_file = open(font_path, 'rb')
txt_file = open(font_path + ".txt", 'wb')
# header = 16 bytes
header = font_file.read(16)
# last 2 header bytes = number of bytes used for characters
# 2 bytes per character, utf-16le
num_bytes_characters = (header[15] << 8) + header[14]
num_characters = num_bytes_characters // 2
# read the characters
for i in range (0, num_characters):
b = font_file.read(2)
txt_file.write(b)
# spritesheet dimensions (square)
dim = int(math.ceil(math.sqrt(num_characters)))
img = Image.new('1', (dim * 16, dim * 16), 0)
pixels = img.load()
for i in range (0, num_characters):
# each character is a 16x16 image, 1 bit per pixel, meaning 32 bytes
char_bytes = font_file.read(32)
row = i // dim
col = i % dim
x = 0
y = 0
# big endian
for byte in char_bytes:
bits = [(byte >> bit) & 1 for bit in range(8 - 1, -1, -1)]
for b in bits:
pixels[16 * col + x, 16 * row + y] = b
x += 1
if x == 16:
x = 0
y += 1
img.save(font_path + '.bmp')
# Create a Mi Band 2 font file from a bmp and txt
def packFont(bmp_path, txt_path, font_path):
print('Packing', font_path)
font_file = open(font_path, 'wb')
bmp_file = open(bmp_path, 'rb')
txt_file = open(txt_path, 'rb')
characters = txt_file.read()
num_characters = len(characters) // 2
# sort the characters, keep track of each index
chars_arr = str(characters, 'utf-16le')
chars_idx = range(num_characters)
chars = list(sorted(zip(chars_arr, chars_idx)))
# header, taken from Mili_pro.ft.en
header = bytearray(binascii.unhexlify('484D5A4B01FFFFFFFF00FFFFFFFFCA00'))
# header, taken from Mili_pro.ft
# header = bytearray(binascii.unhexlify('484D5A4B01FFFFFFFFFFFFFFFFFF7438'))
l = len(characters).to_bytes(2, byteorder='big')
header[14] = l[1]
header[15] = l[0]
font_file.write(header)
img = Image.open(bmp_path)
cols = img.size[0] // 16
rows = img.size[1] // 16
img_rgb = img.convert('RGB')
pixels = img_rgb.load()
for i in range (0, num_characters):
font_file.write(chars[i][0].encode('utf-16le'))
for i in range (0, num_characters):
idx = chars[i][1]
row = idx // cols
col = idx % cols
x = 0
y = 0
while y < 16:
b = 0
for i in range(0, 8):
if pixels[col * 16 + x, row * 16 + y] != (0, 0, 0):
b = b | (1 << (7 - i))
x += 1
if x == 16:
x = 0
y += 1
font_file.write(b.to_bytes(1, 'big'))
if len(sys.argv) == 3 and sys.argv[1] == 'unpack':
unpackFont(sys.argv[2])
elif len(sys.argv) == 5 and sys.argv[1] == 'pack':
packFont(sys.argv[2], sys.argv[3], sys.argv[4])
else:
print('Usage:')
print(' python', sys.argv[0], 'unpack Mili_pro.ft.en')
print(' python', sys.argv[0], 'pack Mili_pro.ft.en.bmp Mili_pro.ft.en.txt out.ft.en')
@dpeddi

This comment has been minimized.

Copy link

commented Sep 13, 2017

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.9 (jessie)
Release: 8.9
Codename: jessie

img.width img.height properties aren't available.
[code]
--- a/mi2font.py
+++ b/mi2font.py
@@ -1,4 +1,5 @@
-#!/usr/bin/python
+#!/usr/bin/python3
+# -- coding: utf-8 --

@@ -83,8 +84,8 @@ def packFont(bmp_path, txt_path, font_path):
font_file.write(characters)

 img = Image.open(bmp_path)
  • cols = img.width // 16
  • rows = img.height // 16
  • cols = img.size[0] // 16
  • rows = img.size[1] // 16
    pixels = img.load()
    [/code]
@joserebelo

This comment has been minimized.

Copy link
Owner Author

commented Sep 14, 2017

@dpeddi thanks, updated :)

@lazarosfs

This comment has been minimized.

Copy link

commented Sep 20, 2017

Good job.
Why do I get this error though:

python mi2font.py unpack Mili_pro.ft
('Unpacking', 'Mili_pro.ft')
Traceback (most recent call last):
File "mi2font.py", line 102, in
unpackFont(sys.argv[2])
File "mi2font.py", line 23, in unpackFont
num_bytes_characters = (header[15] << 8) + header[14]
TypeError: unsupported operand type(s) for <<: 'str' and 'int'

@joserebelo

This comment has been minimized.

Copy link
Owner Author

commented Sep 20, 2017

@lazarosfs can't reproduce the problem here, are you using Python 3?

@lazarosfs

This comment has been minimized.

Copy link

commented Sep 20, 2017

I tried both 2.7 and 3. it only happens on one pc. Can't figure it out.
May I ask, in Mili_pro.ft.txt after the letters follow all binary data.
Do I have to add my characters at the end or replace some japanese for example to pack correcly?

Thankx again for the great work.

@joserebelo

This comment has been minimized.

Copy link
Owner Author

commented Sep 20, 2017

As long as the order in the txt and the bmp files is the same, there should be no problem. You can either replace characters in both files or add new characters at the end, it should work :)

I haven't tried altering the chinese font file, you might need to uncomment line 71 to use the header from Mili_pro.ft instead of Mili_pro.ft.en

@joserebelo

This comment has been minimized.

Copy link
Owner Author

commented Sep 20, 2017

Fixed the problem with the inverted colors.

@tomurbanowicz

This comment has been minimized.

Copy link

commented Sep 21, 2017

tomi@tomi /media/sf_github/tools-master/miband $ python miband.py unpack Mili_pro.ft.en
('Unpacking', 'Mili_pro.ft.en')
Traceback (most recent call last):
File "miband.py", line 108, in
unpackFont(sys.argv[2])
File "miband.py", line 29, in unpackFont
num_bytes_characters = (header[15] << 8) + header[14]
TypeError: unsupported operand type(s) for <<: 'str' and 'int'

@joserebelo

This comment has been minimized.

Copy link
Owner Author

commented Sep 21, 2017

@tomurbanowicz you need Python 3, doesn't seem to work on Python 2.

@joserebelo

This comment has been minimized.

Copy link
Owner Author

commented Sep 21, 2017

Updated the script to ensure the font file is packed with the characters in order.

Thanks to @lazarosfs :)

@lazarosfs

This comment has been minimized.

Copy link

commented Sep 22, 2017

Great work again @josebelo I will test again just to be sure that the packing is done correctly when characters are not in ascending format. :)

@chienkd

This comment has been minimized.

Copy link

commented Apr 4, 2018

Hi, @joserebelo!
I want to add vietnamese character! Can I add to txt file and draw at the end of bmp file?
How Miband 2 knows which character in the ft file is displayed on the screen when it received vietnamese unicode text?
Thanks.

@pm-cz

This comment has been minimized.

Copy link

commented Jun 1, 2018

Hi, @chienkd,
if it is still important for you, I have created tool along the lines of bipfont from https://github.com/amazfitbip/tools, and successfully created a Czech font. It uses same input characters as fonts from Amazfit BIP, so if there is already a repo/font with vietnamese characters for that, it should be able to use it.

The font was uploaded successfully to Miband2 by gadgetbridge, but I was not able to test it in an application which would send characters directly and is known to work. Gadgetbridge's test call displayed utf-8 characters outside of standard 8-bit ASCII with an extra space after them, so I don't know if it is its issue or firmware issue. So I am not sure if I should publish my script to github or not. Most apps provide transliteration to 7-bit ASCII anyway or do not support text display at all.

@rigormortisi

This comment has been minimized.

Copy link

commented Dec 26, 2018

Thank you very much, this script finally allowed me have Georgian characters on my mi band 2, though i struggled a bit, in the end i made it, again thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.