Helvetica Example
This example shows how to use the font-to-python.py program to generate aHelvetica font in a fixed width and variable width version. Note that the fixed width version is easier to place digits on the display, but the variable width version is more aesthetically pleasing for times such as 11:11.
Fixed Width Version
Here are the key points about this version
The font is converted at 40 pixels high. This is about the maximum size that will fit on a 128x64 OLED display with four digits and a colon.
This allows approximately:
- Each digit: ~25-30 pixels wide
- Total for 4 digits: ~100-120 pixels
- Colon and spacing: ~8-10 pixels
- Total width: ~120 pixels (fits within 128 pixel display)
- Height of 40 pixels (fits within 64 pixel display)
To maximize size while ensuring fit:
- Start with 40px height
- If too large, decrease by 2px until perfect fit
- If too small, increase by 2px until just before overflow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 | # Step 1: Convert Helvetica font
# First, download Helvetica.ttf (or use Arial as a free alternative)
# Then run the conversion tool (assuming 40px height which allows ~128px width for 4 digits + colon):
#
# python3 font_to_py.py -x Helvetica.ttf 40 helvetica_40.py
#
# The -x flag creates a fixed-width font which is better for digit alignment
from machine import Pin, SPI
import ssd1306
from writer import Writer
import helvetica_40 # Your converted font
class LargeTimeDisplay:
def __init__(self):
# Initialize SPI and display
self.spi = SPI(0,
sck=Pin(2), # Clock
mosi=Pin(3), # Data
baudrate=100000)
# Display pins
self.dc = Pin(6) # Data/Command
self.rst = Pin(4) # Reset
self.cs = Pin(5) # Chip Select
# Initialize display
self.display = ssd1306.SSD1306_SPI(
128, # Width
64, # Height
self.spi,
self.dc,
self.rst,
self.cs
)
# Initialize writer with large font
self.writer = Writer(self.display, helvetica_40)
# Calculate center position (implemented below)
self.calculate_positions()
def calculate_positions(self):
"""Calculate positions for centered time display"""
# Get character width (should be fixed due to -x flag in conversion)
dummy_char, char_width = helvetica_40.get_ch('0')
# Calculate total width (4 digits + colon)
total_width = (char_width * 4) + 20 # 20 pixels for colon and spacing
# Calculate starting x position to center time
self.start_x = (128 - total_width) // 2
# Calculate y position to center vertically
self.start_y = (64 - helvetica_40.height()) // 2
# Store character width for later use
self.char_width = char_width
def draw_colon(self, x, y):
"""Draw a large colon for separating hours and minutes"""
# Draw two rectangles for the colon
colon_width = 6
colon_height = 6
spacing = 10
# Top dot
self.display.fill_rect(x, y + 10, colon_width, colon_height, 1)
# Bottom dot
self.display.fill_rect(x, y + 10 + spacing + colon_height,
colon_width, colon_height, 1)
def show_time(self, hours, minutes):
"""Display time in large font"""
# Clear display
self.display.fill(0)
# Format hours and minutes
hour_str = f"{hours:02d}"
min_str = f"{minutes:02d}"
# Current x position for drawing
x = self.start_x
# Draw hours
self.writer.set_textpos(x, self.start_y)
self.writer.printstring(hour_str)
# Draw colon
x += self.char_width * 2 + 5 # Move past hours
self.draw_colon(x, self.start_y)
# Draw minutes
x += 15 # Move past colon
self.writer.set_textpos(x, self.start_y)
self.writer.printstring(min_str)
# Update display
self.display.show()
# Example usage
if __name__ == '__main__':
# Create display instance
time_display = LargeTimeDisplay()
# Test display with different times
from time import sleep
test_times = [
(12, 30),
(1, 15),
(5, 45),
(10, 20),
]
# Show each test time for 2 seconds
for hours, minutes in test_times:
time_display.show_time(hours, minutes)
sleep(2)
|
Variable Width Version
Here are the key changes from the Fixed-Width Version:
- Removed -x flag from font conversion which allows variable width characters
- Added methods to calculate individual character widths
- Dynamically calculate positions based on actual character widths
- Adjusted spacing to look better with variable width characters
- Removed leading zero from hours for better appearance
- Added flexible spacing calculations
Notes on Spacing:
- Hours: No leading zero, natural width
- Minutes: Always show leading zero
- Colon: Centered between hours and minutes
- Overall: Centered on display
This allows approximately:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 | from machine import Pin, SPI
import ssd1306
from writer import Writer
import helvetica_40 # Your converted font
class LargeTimeDisplay:
def __init__(self):
# Initialize SPI and display
self.spi = SPI(0,
sck=Pin(2), # Clock
mosi=Pin(3), # Data
baudrate=100000)
# Display pins
self.dc = Pin(6) # Data/Command
self.rst = Pin(4) # Reset
self.cs = Pin(5) # Chip Select
# Initialize display
self.display = ssd1306.SSD1306_SPI(
128, # Width
64, # Height
self.spi,
self.dc,
self.rst,
self.cs
)
# Initialize writer with large font
self.writer = Writer(self.display, helvetica_40)
def get_char_width(self, char):
"""Get width of a specific character"""
_, width = helvetica_40.get_ch(char)
return width
def get_string_width(self, text):
"""Calculate total width of a string"""
return sum(self.get_char_width(char) for char in text)
def draw_colon(self, x, y):
"""Draw a large colon for separating hours and minutes"""
colon_width = 6
colon_height = 6
spacing = 10
# Top dot
self.display.fill_rect(x, y + 10, colon_width, colon_height, 1)
# Bottom dot
self.display.fill_rect(x, y + 10 + spacing + colon_height,
colon_width, colon_height, 1)
return colon_width # Return width for position calculations
def show_time(self, hours, minutes):
"""Display time with variable width font"""
# Clear display
self.display.fill(0)
# Format numbers with leading zeros for minutes only
hour_str = str(hours) # No leading zero for hours
min_str = f"{minutes:02d}" # Leading zero for minutes
# Calculate widths
hour_width = self.get_string_width(hour_str)
min_width = self.get_string_width(min_str)
colon_total_width = 16 # Colon width plus spacing
# Calculate total width
total_width = hour_width + colon_total_width + min_width
# Calculate starting position to center everything
start_x = (128 - total_width) // 2
start_y = (64 - helvetica_40.height()) // 2
# Current x position
x = start_x
# Draw hours
self.writer.set_textpos(x, start_y)
self.writer.printstring(hour_str)
x += hour_width + 5 # Move past hours plus small gap
# Draw colon
self.draw_colon(x, start_y)
x += colon_total_width - 5 # Move past colon (adjusted for spacing)
# Draw minutes
self.writer.set_textpos(x, start_y)
self.writer.printstring(min_str)
# Update display
self.display.show()
# Example usage
if __name__ == '__main__':
# Create display instance
time_display = LargeTimeDisplay()
# Test display with different times
from time import sleep
test_times = [
(1, 11), # Testing narrow digits
(11, 11), # Testing wide number combination
(10, 01), # Testing leading zero
(2, 22), # Testing repeated digits
]
# Show each test time for 2 seconds
for hours, minutes in test_times:
time_display.show_time(hours, minutes)
sleep(2)
|